Category: Blog

  • aws-ecs-devops-using-aws-cdk

    AWS ECS DevOps using AWS CDK

    This repository provides DevOps practices necessary to develop applications based on Amazon Elastic Container Service(ECS) container. Various AWS services are used to support DevOps best practices such as MSA, IaC, CICD, Monitoring and Configuration Management. All necessary cloud resources are modeled and deployed through AWS Cloud Development Kit(CDK). Because AWS CDK abstracts AWS cloud resources as much as possible, we can use it to accelerate DevOps.

    This sample suports both CDK Ver1 and CDK Ver2.

    The basic structure of this CDK project is derived from the following project. Refer to the repository how to design CDK application and orgnize CDK project.

    Korean guide here:

    Other “Using AWS CDK” series can be found at:

    Solution Key Concept

    DevOps encourages various practices to increase development productivity and speed deployment. Representative practies are:

    • MSA(Micro-service Architecture) as a architecture style
    • IaC(Infrastructure as Code) as a way to deal with infrastructure
    • CICD(Continuous Integration Continuous Deploy) Pipeline as a SCM & deployment automation
    • Monitoring Dashboard as a status/usage monitoring
    • Configuration Management for maintaining a strict separation of configuration from code

    Common DevOps Scenario

    A small number of DevOps team(engineers) should be able to provide the following environments(tool/service/infra) easily and quickly and scalably to each service team. At the same time, they must have ownership of the common areas and resources of each service. Conversely, service team(developers) should be able to focus on developing business logic.

    After they(DevOps engineer & Service developer) hold a new service development meeting, DevOps team configures the entire environment(tool/service/infra) and delivers the service development project’s Git repository to the service development team, where the service development team develops business logic. Ultimately, they tune cloud resources through monitoring together.

    devops-rnr

    These are the essential elements that each micro-service development team must have for MSA. This repository abstracts these functions through programmable CDK and provides them through CloudFormation/CDK Stack. In other words, CDK serves as a tool to make the most of these best practices.

    msa-essential-elements

    Solution Architecture

    • Container-based MSA: each micro-services are implemented using AWS ECS(Cluster/Service/Task)
    • Programming-based IaC: all cloud resources are modeld and provisioned using AWS CDK(Typescript)
    • Fully managed CICD: Continuous integration and continuous deploy using AWS Code Series(Pipeline/Commit/Build/Deploy)
    • Fully managed Monitoring: logging, metric, dashboard using Amazon CloudWatch
    • Service Discovery: private DNS service registration & discovery using AWS Cloud Map

    solution-arcitecture

    CDK-Project Build & Deploy

    To efficiently define and provision AWS cloud resources, AWS Cloud Development Kit(CDK) which is an open source software development framework to define your cloud application resources using familiar programming languages is utilized.

    AWSCDKIntro

    Because this solusion is implemented in CDK, we can deploy these cloud resources using CDK CLI. Among the various languages supported, this solution used typescript. Because the types of typescript are very strict, with the help of auto-completion, typescript offers a very nice combination with AWS CDK.

    CDK Useful commands

    • npm install install dependencies
    • cdk list list up stacks
    • cdk deploy deploy this stack to your default AWS account/region
    • cdk diff compare deployed stack with current state
    • cdk synth emits the synthesized CloudFormation template

    Prerequisites

    First of all, AWS Account and IAM User is required. And then the following modules must be installed.

    • AWS CLI: aws configure –profile [profile name]
    • Node.js: node –version
    • AWS CDK: cdk –version
    • jq: jq –version

    Please refer to the kind guide in CDK Workshop.

    Configure AWS Credential

    Please configure your AWS credential to grant AWS roles to your develop PC.

    aws configure --profile [your-profile] 
    AWS Access Key ID [None]: xxxxxx
    AWS Secret Access Key [None]:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
    Default region name [None]: ap-southeast-1 
    Default output format [None]: json
    ...
    ...

    If you don’t know your AWS account information, execute the following commad:

    aws sts get-caller-identity --profile [your-profile]
    ...
    ...
    {
        "UserId": ".............",
        "Account": "75157*******",
        "Arn": "arn:aws:iam::75157*******:user/[your IAM User ID]"
    }

    Check CDK project’s launch config

    The cdk.json file tells CDK Toolkit how to execute your app. Our current entry point for our project is infra/app-main.ts.

    Set up deploy config

    The config/app-config-demo.json file describes how to configure deploy condition & stack condition. First of all, change project configurations(Account, Profile are essential) in config/app-config-demo.json.

    {
        "Project": {
            "Name": "EcsProject",
            "Stage": "Demo",
            "Account": "75157*******",
            "Region": "ap-southeast-1",
            "Profile": "cdk-demo"
        },
        ...
        ...
    }

    And then set the path of the configuration file through an environment variable.

    export APP_CONFIG=config/app-config-demo.json

    Install dependecies & bootstrap

    sh scripts/setup_initial.sh config/app-config-demo.json

    Deploy stacks

    This project has 4 stacks, each of which does the following:

    • EcsProjectDemo-VpcInfraStack: VPC, ECS Cluster, CloudMap Namespace for a base infrastructure
    • EcsProjectDemo-SampleBackendFastapiStack: Private ALB, ECS Service/Task, ECR/CodeCommit, DDB Table, CodePipeline/Build, CloudWatch Dashboard for Backend
    • EcsProjectDemo-SampleFrontendFlaskStack: Public ALB, ECS Service/Task, ECR/CodeCommit, DDB Table, CodePipeline/Build, CloudWatch Dashboard for Frontend
    • EcsProjectDemo-LoadTesterScriptStack: ECS Service/Task for internal-testing

    stack-dependency

    config/app-config-demo.json file describes how to configure each stack. For example backend‘s configuration is like this.

    ...
    ...
    "SampleBackendFastapi": {
        "Name": "SampleBackendFastapiStack",
        "InfraVersion": "'1.0.0'",
        "DockerImageType": "HUB",
        "DockerImageType-Desc": "HUB or ECR or LOCAL",
        
        "PortNumber": 80,
        "InternetFacing": false,
        
        "AppPath": "codes/sample-backend-fastapi",
        "DesiredTasks": 1,
        "Cpu": 256,
        "Memory": 512,
    
        "AutoScalingEnable": false,
        "AutoScalingMinCapacity": 1,
        "AutoScalingMaxCapacity": 2,
        "AutoScalingTargetInvocation": 50,
    
        "TableName": "LogTable",
    
        "AlarmThreshold": 200,
        "SubscriptionEmails": ["kwonyul@amazon.com"]
    },
    ...
    ...

    And frontend‘s configuration is like this.

    ...
    ...
    "SampleFrontendFlask": {
        "Name": "SampleFrontendFlaskStack",
        "InfraVersion": "'1.0.0'",
        "DockerImageType": "HUB",
        "DockerImageType-Desc": "HUB or ECR or LOCAL",
        
        "PortNumber": 80,
        "InternetFacing": true,
    
        "TargetStack": "SampleBackendFastapiStack",
        
        "AppPath": "codes/sample-frontend-flask",
        "DesiredTasks": 1,
        "Cpu": 256,
        "Memory": 512,
    
        "AutoScalingEnable": false,
        "AutoScalingMinCapacity": 1,
        "AutoScalingMaxCapacity": 2,
        "AutoScalingTargetInvocation": 50,
    
        "AlarmThreshold": 200,
        "SubscriptionEmails": ["kwonyul@amazon.com"]
    },
    ...
    ...

    This repository uses python-based containers for convenience only, but you can replace python-based sample stacks with your own container-based stacks later.

    Before deployment, check whether all configurations are ready. Please execute the following command:

    cdk list
    ...
    ...
    ==> CDK App-Config File is config/app-config-demo.json, which is from Environment-Variable.
    EcsProjectDemo-LoadTesterScriptStack
    EcsProjectDemo-SampleBackendFastapiStack
    EcsProjectDemo-SampleFrontendFlaskStack
    EcsProjectDemo-VpcInfraStack
    ...
    ...

    Check if you can see a list of stacks as shown above.

    If there is no problem, finally run the following command:

    sh scripts/deploy_stacks.sh config/app-config-demo.json

    Caution: This solution contains not-free tier AWS services. So be careful about the possible costs.

    It is typically DevOps engineer’s job to deploy these stacks. After deploying these stacks, DevOps engineers need to pass the repository address(CodeCommit name/address) so that service developers can develop their logic in their repository.

    Now you can find deployment results in AWS CloudFormation as shown in the following picture. cloudformation-stacks

    How to test

    Frontend Test

    Because frontend is provided through public ALB(LoadBalancer’s domain name is the output of sh scripts/deploy_stacks), we can connect frontend using web browser.

    ...
    ...
    Outputs:
    EcsProjectDemo-EcsAlbStack.EcsAlbInfraConstrunctServiceLoadBalancerDNSF445CBCD = EcsPr-EcsAl-1TNJ82PAWJ4IV-1937786873.ap-southeast-1.elb.amazonaws.com
    EcsProjectDemo-EcsAlbStack.EcsAlbInfraConstrunctServiceServiceURL290953F6 = http://EcsPr-EcsAl-1TNJ82PAWJ4IV-1937786873.ap-southeast-1.elb.amazonaws.com
    ...
    ...

    frontend-alb-dns

    The initial web page is a php sample screen(in public DockerHub) as frontend service team haven’t uploaded their source code yet. initial-web-page

    And CloudWatch’s dashboard provides the current monitoring status like this. frontend-dashboard

    Backend Test

    Because backend is provided through private ALB, we can not use web browser. LoadTesterScriptStack is provided for testing that internally in VPC, which is sending a lot of requests within the same VPC. codes/load-tester-script/app/entrypoint.sh file describes how to work in docker container, where two types of URL(URL_ALB, URL_NAMESPACE) are utilized like this.

    #!/bin/sh
    
    echo "--TEST CONFIGURATION--"
    echo "RequestCount: $RequestCount"
    echo "SleepPeriodInSec: $SleepPeriodInSec"
    
    echo "--TARGET URL--"
    export URL_ALB=http://$AlbDnsName/items
    export URL_NAMESPACE=http://$TargetServiceName.$Namespace/items
    echo "URL_ALB>> $URL_ALB"
    echo "URL_NAMESPACE>> $URL_NAMESPACE"
    
    
    function ab_function {
        echo --ALB-RESPONSE-TEST--
        curl -X GET $URL_ALB
        ab -n $RequestCount -c 1 $URL_ALB
    
        echo --NS-RESPONSE-TEST--
        curl -X GET $URL_NAMESPACE
        ab -n $RequestCount -c 1 $URL_NAMESPACE
    
    }
    
    echo "--START LOAD TEST--"
    while true; do ab_function; sleep $SleepPeriodInSec; done

    If you want to change request-load, please change these values(DesiredTasks, RequestCount, SleepPeriodInSec) in app-config-demo.json and re-deploy LoadTesterScriptStack stack.

    "LoadTesterScript": {
        "Name": "LoadTesterScriptStack",
    
        "TargetStack": "SampleBackendFastapiStack",
    
        "AppPath": "codes/load-tester-script",
        "DesiredTasks": 1,
        "RequestCount": 10,
        "SleepPeriodInSec": 1
    }

    And CloudWatch’s dashboard provides the current monitoring status like this. backend-dashboard

    Caution: Because the current DDB table’s capacity is very low, RequestCount is also low. The answer to this can be found through below graph. That is, DDB table’s throttle metric continued to occur and the backend could not respond quickly, resulting in low TPS. To solve this issue, please change the ecs-infra-consts.ts file to increase RCU in DDB Table. What should we do to control these values from the outside? I’ll leave this as a homework assignment.

    backend-throttle

    How to update frontend/backend

    DevOps Team(Engineers)

    DevOps team(engineers) created CodeCommit(source repoistory)/ECR(docker repository) repository throught AWS CDK’s stacks. Please visit CodeCommit in AWS management web console, note the repository address in CodeCommit and share it with backend developers and frontend developers. codecommit-repo

    Service Team(Developers)

    When frontend/backend service team(developers) receives the Git address, they clone each address and start developing the project. When each developer commit and puth to Git, CICD pipeline is triggered.

    For example, backend service developer’s project looks like this, where code/sample-backend-fastapi path must match AppPath in app-config-demo.json.

    backend-project

    For example, frontend service developer’s project looks like this, where code/sample-frontend-flaskweb path must match AppPath in app-config-demo.json.

    frontend-project

    For convenience, I have prepared sample codes in codes/sample-backend-fastapi and codes/sample-frontend-flask path, but actually these codes should be managed in separate repositories.

    Once CodePipeline starts, this builds each docker image, and push it to ECR, and wait for Review button to be clicked on ECS Deployments.

    CodePipeline is in In Process. code-pipeline-inprogress

    CodePipeline is waiting for Review button to be clicked. code-pipeline-review

    After provisioning, you can check the updated web page like the following screen. new-web-page

    How to update infrastructure

    After the business logic of each service is deployed through ECR, each stack’s configuration must be changed so that the infrastructure deployment also refers to ECR.

    Just change DockerImageType from HUB to ECR in each stack configuration below.

    "SampleBackendFastapi": {
        "Name": "SampleBackendFastapiStack",
        ...
        ...
        "DockerImageType": "HUB", <----- ECR
        "DockerImageType-Desc": "HUB or ECR or LOCAL",
        ...
        ...
    },

    How to add a new service

    Just add a new stack configuration in config/app-config-demo.json. And then instantiate EcsAlbServiceStack in infra/app-main.ts with the new stack configuration.

    For example, if your new stack is named UserBackendSpringStack, you can work in pairs like this:

    configuration in config/app-config-demo.json

    ...
    ...
    "UserBackendSpring": {
        "Name": "UserBackendSpringStack",
        "InfraVersion": "'1.0.0'",
        "DockerImageType": "HUB",
        "DockerImageType-Desc": "HUB or ECR or LOCAL",
        
        "PortNumber": 80,
        "InternetFacing": false,
        
        "AppPath": "codes/user-backend-spring",
        "DesiredTasks": 1,
        "Cpu": 1024,
        "Memory": 2048,
    
        "AutoScalingEnable": false,
        "AutoScalingMinCapacity": 1,
        "AutoScalingMaxCapacity": 2,
        "AutoScalingTargetInvocation": 50,
    
        "TableName": "LogTable",
    
        "AlarmThreshold": 200,
        "SubscriptionEmails": ["kwonyul@amazon.com"]
    },
    ...
    ...

    instantiation in infra/app-main.ts

    ...
    ...
    new EcsAlbServiceStack(appContext, appContext.appConfig.Stack.UserBackendSpring);
    ...
    ...

    Since EcsAlbServiceStack abstracts each service(CodeCommit, ECR, ECS Service/Task, Dashboard, CICD Pipeline), everything can be automated by instantiating objects with proper configuration. This is one of the great advantages of AWS CDK.

    How to extend service

    If you want to change or extend the functionality for EcsAlbServiceStack, define a new class that inherits from EcsAlbServiceStack.

    For example, if you want to replace the database from DDB table to RDS database, this is the right situation.

    How to clean up

    Execute the following command, which will destroy all resources except ECR-Repository and DynamoDB Tables. So destroy these resources in AWS web console manually.

    sh ./scripts/destroy_stacks.sh config/app-config-demo.json

    In particular, LoadTesterScriptStack is currently under load-testing and should be deleted as soon as possible like this.

    cdk destroy *LoadTesterScriptStack --profile [optional: your-profile-name]

    Security

    See CONTRIBUTING for more information.

    License Summary

    The documentation is made available under the Creative Commons Attribution-ShareAlike 4.0 International License. See the LICENSE file.

    The sample code within this documentation is made available under the MIT-0 license. See the LICENSE-SAMPLECODE file.

    Visit original content creator repository https://github.com/aws-samples/aws-ecs-devops-using-aws-cdk
  • aws-ecs-devops-using-aws-cdk

    AWS ECS DevOps using AWS CDK

    This repository provides DevOps practices necessary to develop applications based on Amazon Elastic Container Service(ECS) container. Various AWS services are used to support DevOps best practices such as MSA, IaC, CICD, Monitoring and Configuration Management. All necessary cloud resources are modeled and deployed through AWS Cloud Development Kit(CDK). Because AWS CDK abstracts AWS cloud resources as much as possible, we can use it to accelerate DevOps.

    This sample suports both CDK Ver1 and CDK Ver2.

    The basic structure of this CDK project is derived from the following project. Refer to the repository how to design CDK application and orgnize CDK project.

    Korean guide here:

    Other “Using AWS CDK” series can be found at:

    Solution Key Concept

    DevOps encourages various practices to increase development productivity and speed deployment. Representative practies are:

    • MSA(Micro-service Architecture) as a architecture style
    • IaC(Infrastructure as Code) as a way to deal with infrastructure
    • CICD(Continuous Integration Continuous Deploy) Pipeline as a SCM & deployment automation
    • Monitoring Dashboard as a status/usage monitoring
    • Configuration Management for maintaining a strict separation of configuration from code

    Common DevOps Scenario

    A small number of DevOps team(engineers) should be able to provide the following environments(tool/service/infra) easily and quickly and scalably to each service team. At the same time, they must have ownership of the common areas and resources of each service. Conversely, service team(developers) should be able to focus on developing business logic.

    After they(DevOps engineer & Service developer) hold a new service development meeting, DevOps team configures the entire environment(tool/service/infra) and delivers the service development project’s Git repository to the service development team, where the service development team develops business logic. Ultimately, they tune cloud resources through monitoring together.

    devops-rnr

    These are the essential elements that each micro-service development team must have for MSA. This repository abstracts these functions through programmable CDK and provides them through CloudFormation/CDK Stack. In other words, CDK serves as a tool to make the most of these best practices.

    msa-essential-elements

    Solution Architecture

    • Container-based MSA: each micro-services are implemented using AWS ECS(Cluster/Service/Task)
    • Programming-based IaC: all cloud resources are modeld and provisioned using AWS CDK(Typescript)
    • Fully managed CICD: Continuous integration and continuous deploy using AWS Code Series(Pipeline/Commit/Build/Deploy)
    • Fully managed Monitoring: logging, metric, dashboard using Amazon CloudWatch
    • Service Discovery: private DNS service registration & discovery using AWS Cloud Map

    solution-arcitecture

    CDK-Project Build & Deploy

    To efficiently define and provision AWS cloud resources, AWS Cloud Development Kit(CDK) which is an open source software development framework to define your cloud application resources using familiar programming languages is utilized.

    AWSCDKIntro

    Because this solusion is implemented in CDK, we can deploy these cloud resources using CDK CLI. Among the various languages supported, this solution used typescript. Because the types of typescript are very strict, with the help of auto-completion, typescript offers a very nice combination with AWS CDK.

    CDK Useful commands

    • npm install install dependencies
    • cdk list list up stacks
    • cdk deploy deploy this stack to your default AWS account/region
    • cdk diff compare deployed stack with current state
    • cdk synth emits the synthesized CloudFormation template

    Prerequisites

    First of all, AWS Account and IAM User is required. And then the following modules must be installed.

    • AWS CLI: aws configure –profile [profile name]
    • Node.js: node –version
    • AWS CDK: cdk –version
    • jq: jq –version

    Please refer to the kind guide in CDK Workshop.

    Configure AWS Credential

    Please configure your AWS credential to grant AWS roles to your develop PC.

    aws configure --profile [your-profile] 
    AWS Access Key ID [None]: xxxxxx
    AWS Secret Access Key [None]:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
    Default region name [None]: ap-southeast-1 
    Default output format [None]: json
    ...
    ...

    If you don’t know your AWS account information, execute the following commad:

    aws sts get-caller-identity --profile [your-profile]
    ...
    ...
    {
        "UserId": ".............",
        "Account": "75157*******",
        "Arn": "arn:aws:iam::75157*******:user/[your IAM User ID]"
    }

    Check CDK project’s launch config

    The cdk.json file tells CDK Toolkit how to execute your app. Our current entry point for our project is infra/app-main.ts.

    Set up deploy config

    The config/app-config-demo.json file describes how to configure deploy condition & stack condition. First of all, change project configurations(Account, Profile are essential) in config/app-config-demo.json.

    {
        "Project": {
            "Name": "EcsProject",
            "Stage": "Demo",
            "Account": "75157*******",
            "Region": "ap-southeast-1",
            "Profile": "cdk-demo"
        },
        ...
        ...
    }

    And then set the path of the configuration file through an environment variable.

    export APP_CONFIG=config/app-config-demo.json

    Install dependecies & bootstrap

    sh scripts/setup_initial.sh config/app-config-demo.json

    Deploy stacks

    This project has 4 stacks, each of which does the following:

    • EcsProjectDemo-VpcInfraStack: VPC, ECS Cluster, CloudMap Namespace for a base infrastructure
    • EcsProjectDemo-SampleBackendFastapiStack: Private ALB, ECS Service/Task, ECR/CodeCommit, DDB Table, CodePipeline/Build, CloudWatch Dashboard for Backend
    • EcsProjectDemo-SampleFrontendFlaskStack: Public ALB, ECS Service/Task, ECR/CodeCommit, DDB Table, CodePipeline/Build, CloudWatch Dashboard for Frontend
    • EcsProjectDemo-LoadTesterScriptStack: ECS Service/Task for internal-testing

    stack-dependency

    config/app-config-demo.json file describes how to configure each stack. For example backend‘s configuration is like this.

    ...
    ...
    "SampleBackendFastapi": {
        "Name": "SampleBackendFastapiStack",
        "InfraVersion": "'1.0.0'",
        "DockerImageType": "HUB",
        "DockerImageType-Desc": "HUB or ECR or LOCAL",
        
        "PortNumber": 80,
        "InternetFacing": false,
        
        "AppPath": "codes/sample-backend-fastapi",
        "DesiredTasks": 1,
        "Cpu": 256,
        "Memory": 512,
    
        "AutoScalingEnable": false,
        "AutoScalingMinCapacity": 1,
        "AutoScalingMaxCapacity": 2,
        "AutoScalingTargetInvocation": 50,
    
        "TableName": "LogTable",
    
        "AlarmThreshold": 200,
        "SubscriptionEmails": ["kwonyul@amazon.com"]
    },
    ...
    ...

    And frontend‘s configuration is like this.

    ...
    ...
    "SampleFrontendFlask": {
        "Name": "SampleFrontendFlaskStack",
        "InfraVersion": "'1.0.0'",
        "DockerImageType": "HUB",
        "DockerImageType-Desc": "HUB or ECR or LOCAL",
        
        "PortNumber": 80,
        "InternetFacing": true,
    
        "TargetStack": "SampleBackendFastapiStack",
        
        "AppPath": "codes/sample-frontend-flask",
        "DesiredTasks": 1,
        "Cpu": 256,
        "Memory": 512,
    
        "AutoScalingEnable": false,
        "AutoScalingMinCapacity": 1,
        "AutoScalingMaxCapacity": 2,
        "AutoScalingTargetInvocation": 50,
    
        "AlarmThreshold": 200,
        "SubscriptionEmails": ["kwonyul@amazon.com"]
    },
    ...
    ...

    This repository uses python-based containers for convenience only, but you can replace python-based sample stacks with your own container-based stacks later.

    Before deployment, check whether all configurations are ready. Please execute the following command:

    cdk list
    ...
    ...
    ==> CDK App-Config File is config/app-config-demo.json, which is from Environment-Variable.
    EcsProjectDemo-LoadTesterScriptStack
    EcsProjectDemo-SampleBackendFastapiStack
    EcsProjectDemo-SampleFrontendFlaskStack
    EcsProjectDemo-VpcInfraStack
    ...
    ...

    Check if you can see a list of stacks as shown above.

    If there is no problem, finally run the following command:

    sh scripts/deploy_stacks.sh config/app-config-demo.json

    Caution: This solution contains not-free tier AWS services. So be careful about the possible costs.

    It is typically DevOps engineer’s job to deploy these stacks. After deploying these stacks, DevOps engineers need to pass the repository address(CodeCommit name/address) so that service developers can develop their logic in their repository.

    Now you can find deployment results in AWS CloudFormation as shown in the following picture. cloudformation-stacks

    How to test

    Frontend Test

    Because frontend is provided through public ALB(LoadBalancer’s domain name is the output of sh scripts/deploy_stacks), we can connect frontend using web browser.

    ...
    ...
    Outputs:
    EcsProjectDemo-EcsAlbStack.EcsAlbInfraConstrunctServiceLoadBalancerDNSF445CBCD = EcsPr-EcsAl-1TNJ82PAWJ4IV-1937786873.ap-southeast-1.elb.amazonaws.com
    EcsProjectDemo-EcsAlbStack.EcsAlbInfraConstrunctServiceServiceURL290953F6 = http://EcsPr-EcsAl-1TNJ82PAWJ4IV-1937786873.ap-southeast-1.elb.amazonaws.com
    ...
    ...

    frontend-alb-dns

    The initial web page is a php sample screen(in public DockerHub) as frontend service team haven’t uploaded their source code yet. initial-web-page

    And CloudWatch’s dashboard provides the current monitoring status like this. frontend-dashboard

    Backend Test

    Because backend is provided through private ALB, we can not use web browser. LoadTesterScriptStack is provided for testing that internally in VPC, which is sending a lot of requests within the same VPC. codes/load-tester-script/app/entrypoint.sh file describes how to work in docker container, where two types of URL(URL_ALB, URL_NAMESPACE) are utilized like this.

    #!/bin/sh
    
    echo "--TEST CONFIGURATION--"
    echo "RequestCount: $RequestCount"
    echo "SleepPeriodInSec: $SleepPeriodInSec"
    
    echo "--TARGET URL--"
    export URL_ALB=http://$AlbDnsName/items
    export URL_NAMESPACE=http://$TargetServiceName.$Namespace/items
    echo "URL_ALB>> $URL_ALB"
    echo "URL_NAMESPACE>> $URL_NAMESPACE"
    
    
    function ab_function {
        echo --ALB-RESPONSE-TEST--
        curl -X GET $URL_ALB
        ab -n $RequestCount -c 1 $URL_ALB
    
        echo --NS-RESPONSE-TEST--
        curl -X GET $URL_NAMESPACE
        ab -n $RequestCount -c 1 $URL_NAMESPACE
    
    }
    
    echo "--START LOAD TEST--"
    while true; do ab_function; sleep $SleepPeriodInSec; done

    If you want to change request-load, please change these values(DesiredTasks, RequestCount, SleepPeriodInSec) in app-config-demo.json and re-deploy LoadTesterScriptStack stack.

    "LoadTesterScript": {
        "Name": "LoadTesterScriptStack",
    
        "TargetStack": "SampleBackendFastapiStack",
    
        "AppPath": "codes/load-tester-script",
        "DesiredTasks": 1,
        "RequestCount": 10,
        "SleepPeriodInSec": 1
    }

    And CloudWatch’s dashboard provides the current monitoring status like this. backend-dashboard

    Caution: Because the current DDB table’s capacity is very low, RequestCount is also low. The answer to this can be found through below graph. That is, DDB table’s throttle metric continued to occur and the backend could not respond quickly, resulting in low TPS. To solve this issue, please change the ecs-infra-consts.ts file to increase RCU in DDB Table. What should we do to control these values from the outside? I’ll leave this as a homework assignment.

    backend-throttle

    How to update frontend/backend

    DevOps Team(Engineers)

    DevOps team(engineers) created CodeCommit(source repoistory)/ECR(docker repository) repository throught AWS CDK’s stacks. Please visit CodeCommit in AWS management web console, note the repository address in CodeCommit and share it with backend developers and frontend developers. codecommit-repo

    Service Team(Developers)

    When frontend/backend service team(developers) receives the Git address, they clone each address and start developing the project. When each developer commit and puth to Git, CICD pipeline is triggered.

    For example, backend service developer’s project looks like this, where code/sample-backend-fastapi path must match AppPath in app-config-demo.json.

    backend-project

    For example, frontend service developer’s project looks like this, where code/sample-frontend-flaskweb path must match AppPath in app-config-demo.json.

    frontend-project

    For convenience, I have prepared sample codes in codes/sample-backend-fastapi and codes/sample-frontend-flask path, but actually these codes should be managed in separate repositories.

    Once CodePipeline starts, this builds each docker image, and push it to ECR, and wait for Review button to be clicked on ECS Deployments.

    CodePipeline is in In Process. code-pipeline-inprogress

    CodePipeline is waiting for Review button to be clicked. code-pipeline-review

    After provisioning, you can check the updated web page like the following screen. new-web-page

    How to update infrastructure

    After the business logic of each service is deployed through ECR, each stack’s configuration must be changed so that the infrastructure deployment also refers to ECR.

    Just change DockerImageType from HUB to ECR in each stack configuration below.

    "SampleBackendFastapi": {
        "Name": "SampleBackendFastapiStack",
        ...
        ...
        "DockerImageType": "HUB", <----- ECR
        "DockerImageType-Desc": "HUB or ECR or LOCAL",
        ...
        ...
    },

    How to add a new service

    Just add a new stack configuration in config/app-config-demo.json. And then instantiate EcsAlbServiceStack in infra/app-main.ts with the new stack configuration.

    For example, if your new stack is named UserBackendSpringStack, you can work in pairs like this:

    configuration in config/app-config-demo.json

    ...
    ...
    "UserBackendSpring": {
        "Name": "UserBackendSpringStack",
        "InfraVersion": "'1.0.0'",
        "DockerImageType": "HUB",
        "DockerImageType-Desc": "HUB or ECR or LOCAL",
        
        "PortNumber": 80,
        "InternetFacing": false,
        
        "AppPath": "codes/user-backend-spring",
        "DesiredTasks": 1,
        "Cpu": 1024,
        "Memory": 2048,
    
        "AutoScalingEnable": false,
        "AutoScalingMinCapacity": 1,
        "AutoScalingMaxCapacity": 2,
        "AutoScalingTargetInvocation": 50,
    
        "TableName": "LogTable",
    
        "AlarmThreshold": 200,
        "SubscriptionEmails": ["kwonyul@amazon.com"]
    },
    ...
    ...

    instantiation in infra/app-main.ts

    ...
    ...
    new EcsAlbServiceStack(appContext, appContext.appConfig.Stack.UserBackendSpring);
    ...
    ...

    Since EcsAlbServiceStack abstracts each service(CodeCommit, ECR, ECS Service/Task, Dashboard, CICD Pipeline), everything can be automated by instantiating objects with proper configuration. This is one of the great advantages of AWS CDK.

    How to extend service

    If you want to change or extend the functionality for EcsAlbServiceStack, define a new class that inherits from EcsAlbServiceStack.

    For example, if you want to replace the database from DDB table to RDS database, this is the right situation.

    How to clean up

    Execute the following command, which will destroy all resources except ECR-Repository and DynamoDB Tables. So destroy these resources in AWS web console manually.

    sh ./scripts/destroy_stacks.sh config/app-config-demo.json

    In particular, LoadTesterScriptStack is currently under load-testing and should be deleted as soon as possible like this.

    cdk destroy *LoadTesterScriptStack --profile [optional: your-profile-name]

    Security

    See CONTRIBUTING for more information.

    License Summary

    The documentation is made available under the Creative Commons Attribution-ShareAlike 4.0 International License. See the LICENSE file.

    The sample code within this documentation is made available under the MIT-0 license. See the LICENSE-SAMPLECODE file.

    Visit original content creator repository https://github.com/aws-samples/aws-ecs-devops-using-aws-cdk
  • better-alipay-go

    Better Aliapy Go

    Go Report Card GoDoc

    A better Alipay SDK for Golang. With First-class function, without the tears 😢 GoDoc

    • Thread safe
    • Hooks friendly
    • Tracing friendly
    • Dynamic configuration friendly
    • Multiple configuration friendly

    一个更好的支付宝(Alipay)SDK。函数优先,没有眼泪😢 中文文档

    • 多线程安全
    • Hooks
    • 链路跟踪
    • 动态配置
    • 多个配置

    Contents

    Installation

    To install Better-Alipay-Go package, you need to install Go and set your Go workspace first.

    1. The first need Go installed, then you can use the below Go command to install it.
    $ go get -u github.com/WenyXu/better-alipay-go
    1. Import it in your code:
    import "github.com/WenyXu/better-alipay-go"

    Quick Start

    package main
    import (
        alipay "github.com/WenyXu/better-alipay-go"
        "github.com/WenyXu/better-alipay-go/options"
        "github.com/WenyXu/better-alipay-go/global"
        "github.com/WenyXu/better-alipay-go/m"
        "os"
    )
    func main(){
    	// init a default client with a app configuration
        s := alipay.Default(
            options.AppId(os.Getenv("APP_ID")),
            options.PrivateKey(os.Getenv("PrivateKey")),
            // Depended on you AppCert type 
            // if you'd like load the cert form []byte
            // just use:
            // options.AppCertBytes()
            // if you already save cert sn at somewhere
            // just use:
            // options.AppCertSn()
            options.AppCertPath("./cert_file/appCertPublicKey.crt"),
            // similar to the AppCertPath
            // also provide:
            // options.RootCertBytes()
            // options.RootCertSn()
            options.RootCertPath("./cert_file/alipayRootCert.crt"),
            // similar to the AppCertPath
            // also provide:
            // options.PublicCertBytes()
            // options.PublicCertSn()
            options.PublicCertPath("./cert_file/alipayCertPublicKey_RSA2.crt"),
            options.Production(false),
            // or global.PKCS1
            options.PrivateKeyType(global.PKCS8),
            // or global.RSA
            options.SignType(global.RSA2),
        )
    	
        // of course, you can using a struct instead
        // var resp AlipayTradeCreateResponse
        resp := make(map[string]interface{})
        _ = s.Request("alipay.trade.create",m.NewMap(func(param m.M) {
              param.
                  Set("subject", "网站测试支付").
                  Set("buyer_id", "2088802095984694").
                  Set("out_trade_no", "123456786543").
                  Set("total_amount", "88.88"),
        }), &resp)
    
        // without biz-content request
        _ = s.Request(global.AlipaySystemOauthToken, m.NewMap(func(param m.M) {
            // set key value as public params 
        	param.
            	Set("grant_type", "authorization_code").
            	Set("code", "3a06216ac8f84b8c93507bb9774bWX11")
        }),
            &resp,
            options.SetMakeReqFunc(options.WithoutBizContentMakeReqFunc),
        )
    	
    }
        

    Global Configuration

    You can use following functions to configure global configuration.

    package main
    
    import (
    	"github.com/WenyXu/better-alipay-go/options"
    	"time"
    )
    
    func main() {
    	// set Default Transport 
    	loc, err := time.LoadLocation("Asia/Shanghai")
    	if err != nil {
    		panic(err)
    	}
    	options.SetDefaultLocation(options.SetLocation(loc))
    	
    	// set Default Transport which implement http.RoundTripper interface
    	transport := YourCustomTransport()
    	options.SetDefaultTransport(transport)
    
    	// set Default MakeRequestFunc which implement options.MakeRequestFunc func
    	options.SetDefaultMakeReqFunc(yourMakeReqFunc)
    
    	// set Default DecFunc which implement options.DecFunc func
    	options.SetDefaultDecFunc(yourDecFunc)
    	
    	// set Default Logger which implement logger.Logger interface
    	// built-in :
    	// logger.NullLogger
    	// logger.StdLogger
    	options.SetDefaultLogger(yourLogger)
    }

    After above configuring, alipay.New / alipay.Default / options.newOptions will return new Options with your configured.

    // options.go 
    func newOptions(opts ...Option) Options {
    	opt := Options{
    		Transport: DefaultTransport,
    		Context:   context.Background(),
    		MakeReq:   DefaultMakeReqFunc,
    		Dec:       DefaultDecFunc,
    		Logger:    DefaultLogger,
    	}
    	for _, o := range opts {
    		o(&opt)
    	}
    	return opt
    }

    Dynamic Configuration

    If your application have multiple configurations, you can configure configuration dynamically.

    When you call the Request func, it will make a copy form your current Options, then modify the Options. generally, it will be thread-safe.

    Basic Usage

    Configure per request

    package main
    import (
        alipay "github.com/WenyXu/better-alipay-go"
        "github.com/WenyXu/better-alipay-go/options"
        "github.com/WenyXu/better-alipay-go/global"
        "github.com/WenyXu/better-alipay-go/m"
        "os"
    )
    func main(){
    	// init a default client with a app configuration
        s := alipay.Default(
            options.AppId(os.Getenv("APP_ID")),
            options.PrivateKey(os.Getenv("PrivateKey")),
            options.AppCertPath("./cert_file/appCertPublicKey.crt"),
            options.RootCertPath("./cert_file/alipayRootCert.crt"),
            options.PublicCertPath("./cert_file/alipayCertPublicKey_RSA2.crt"),
            options.Production(false),
            options.PrivateKeyType(global.PKCS8),
            options.SignType(global.RSA2),
        )
    	
        // of course, you can using a struct instead
        // var resp AlipayTradeCreateResponse
        resp := make(map[string]interface{})
    	s.Request("alipay.trade.create",m.NewMap(func(param m.M) {
                param.
                    Set("subject", "网站测试支付").
                    Set("buyer_id", "2088802095984694").
                    Set("out_trade_no", "123456786543").
                    Set("total_amount", "88.88")
            }),
            &resp,
            // dynamic configuration
            options.AppAuthToken("your-app-auth-token"),
            options.AuthToken("your-auth-token"),
    	)
    }

    Advanced Usage

    Configure with func which implement options.Option.

        // implement options.Option
        // type Option func(*Options)
        func CustomOption(o *options.Options) {
            // modify your app config 
            // using 
            o.Config.AppId="whatever"
        }
        
        ...
    
        func main(){
            // use custom option in New or Default func 
    		s := alipay.Default(
    			...,
    		    CustomOption
            )
            // or use custom option in Request of MakeParam func 
            s.Request("alipay.trade.create",m.NewMap(func(param m.M) {
                  param.
                  Set("subject", "网站测试支付").
                  Set("buyer_id", "2088802095984694").
                  Set("out_trade_no", "123456786543").
                  Set("total_amount", "88.88")
              }),
              &resp,
              // your custom option
              CustomOption,
            )
        }   
        ...
        
        

    Use a client without default app configuration and configure per request.

    package main
    import (
        alipay "github.com/WenyXu/better-alipay-go"
        "github.com/WenyXu/better-alipay-go/options"
        "github.com/WenyXu/better-alipay-go/global"
        "github.com/WenyXu/better-alipay-go/m"
    )
    
    func main(){
    	// init a empty client 
        s := alipay.Default()
        
        // of course, you can using a struct instead
        // var resp AlipayTradeCreateResponse
        resp := make(map[string]interface{})
    	s.Request("alipay.trade.create",m.NewMap(func(param m.M) {
    		param.
    			Set("subject", "网站测试支付").
    			Set("buyer_id", "2088802095984694").
    			Set("out_trade_no", "123456786543").
    			Set("total_amount", "88.88")
    	    }),
    	    &resp,
    	    // dynamic configuration pre Request func
    	    options.AppId(os.Getenv("APP_ID")),
    	    options.PrivateKey(os.Getenv("PrivateKey")),
    	    options.AppCertPath("./cert_file/appCertPublicKey.crt"),
                options.RootCertPath("./cert_file/alipayRootCert.crt"),
                options.PublicCertPath("./cert_file/alipayCertPublicKey_RSA2.crt"),
                options.Production(false),
                options.PrivateKeyType(global.PKCS8),
                options.SignType(global.RSA2),
    	)
    }

    Load configuration form database or somewhere, and configure dynamically at per request.

    package main
    import (
    	alipay "github.com/WenyXu/better-alipay-go"
    	"github.com/WenyXu/better-alipay-go/options"
    	"github.com/WenyXu/better-alipay-go/m"
    )
    
    func MakeAppConfig(yourConfig ConfigEntity) options.Option {
    	return func(o *options.Options) {
    		// modify your app config 
    		// using 
    		o.Config.AppId=yourConfig.AppId
    		...
    	}
    }
    func main()  {
    	// init a empty client 
    	s := alipay.Default()
    
    	// you config entity
    	config:=ReadFormSomeWhere()
        
    	// of course, you can using a struct instead
    	// var resp AlipayTradeCreateResponse
    	resp := make(map[string]interface{})
    	s.Request("alipay.trade.create",m.NewMap(func(param m.M) {
                  param.
                      Set("subject", "网站测试支付").
                      Set("buyer_id", "2088802095984694").
                      Set("out_trade_no", "123456786543").
                      Set("total_amount", "88.88")
    	    }),
            &resp,
            // dynamic configuration pre Request func
            MakeAppConfig(config),
    	)
        
    }

    Hooks

    In Options, we provide hooks which run before request started, and after response received, so you can do something like inject context, tracing’s span etc. it is just similar to web middleware. We provide a sample here, help you have a concept.

    // options.go
    // DefaultBeforeFunc log the request body
    func DefaultBeforeFunc(ctx context.Context, req *http.Request) context.Context {
        body, err := ioutil.ReadAll(req.Body)
        if err != nil {
            fmt.Printf("Read Request body with error: %s", err.Error())
            return ctx
        }
        req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
        fmt.Println(string(body))
        return ctx
    }
    
    // DefaultAfterFunc log the response body
    func DefaultAfterFunc(ctx context.Context, resp *http.Response) context.Context {
        body, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            fmt.Printf("Read Response body with error: %s", err.Error())
            return ctx
        }
        resp.Body = ioutil.NopCloser(bytes.NewBuffer(body))
        fmt.Println(string(body))
        return ctx
    }

    The Request fun will run before-hooks before the request started, and run after-hook after response received.

    //alipay.go
    // Do Request
    func (s service) Request(method string, param m.M, response interface{}, opts ...options.Option) (err error) {
        copyOpts := s.opts.Copy()
        
        ...
        
        // run before hooks before request started
        for _, f := range copyOpts.Before {
            ctx = f(ctx, req)
        }
        
        ...
        
        // do request
        resp, err := copyOpts.Transport.RoundTrip(req)
        
        
        ...
        
        // run after hooks after response received
        for _, f := range copyOpts.After {
            ctx = f(ctx, resp)
        }
        
        ...
    }

    Tracing

    You can inject context, when your call a Request func, and use hook to finish the tracing.

    package main
    
    import (
    	"context"
    	alipay "github.com/WenyXu/better-alipay-go"
    	"github.com/WenyXu/better-alipay-go/options"
    	"github.com/WenyXu/better-alipay-go/m"
    	"github.com/opentracing/opentracing-go"
    	"net/http"
    )
    
    func main() {
    	s := alipay.Default()
    	// get trace instance
    	trace := yourTracingInstance()
    	resp := make(map[string]interface{})
    	s.Request("alipay.trade.create", m.NewMap(func(param m.M) {
    		param.
    			Set("subject", "网站测试支付").
    			Set("buyer_id", "2088802095984694").
    			Set("out_trade_no", "123456786543").
    			Set("total_amount", "88.88")
    	}),
    	&resp,
    	// inject your tracing context
    	func(trace opentracing.Tracer) options.Option {
    		return func(o *options.Options) {
    			sp := opentracing.StartSpan("tarcing name")
    			// injected
    			o.Context = context.WithValue(o.Context,"span-key",sp)
    		}
    	}(trace),
    	// handle span finish
    	options.AppendAfterFunc(func(c context.Context, response *http.Response) context.Context {
                sp, ok := c.Get("span-key")
                if ok {
                    // span finish
                    sp.Finish()
                }
    		    return c
    	    }),
    	)
    
    }

    Built-in Methods constant and Response structs

    Following methods:

            // https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.enterinfo.sync
    	AlipayEcoMyCarParkingEnterInfoSync = "alipay.eco.mycar.parking.enterinfo.sync"
    
    	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.exitinfo.sync
    	AlipayEcoMyCarParkingExitInfoSync = "alipay.eco.mycar.parking.exitinfo.sync"
    
    	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.vehicle.query
    	AlipayEcoMyCarParkingVehicleQuery = "alipay.eco.mycar.parking.vehicle.query"
    
    	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.order.sync
    	AlipayEcoMyCarParkingOrderSync = "alipay.eco.mycar.parking.order.sync"
    
    	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.order.update
    	AlipayEcoMyCarParkingOrderUpdate = "alipay.eco.mycar.parking.order.update"
    
    	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.config.set
    	AlipayEcoMyCarParkingConfigSet = "alipay.eco.mycar.parking.config.set"
    
    	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.parkinglotinfo.update
    	AlipayEcoMyCarParkingParkingLotInfoUpdate = "alipay.eco.mycar.parking.parkinglotinfo.update"
    
    	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.parkinglotinfo.create
    	AlipayEcoMyCarParkingParkingLotInfoCreate = "alipay.eco.mycar.parking.parkinglotinfo.create"
    
    	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.parkinglotinfo.query
    	AlipayEcoMyCarParkingParkingLotInfoQuery = "alipay.eco.mycar.parking.parkinglotinfo.query"
    
    	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.order.pay
    	AlipayEcoMyCarParkingOrderPay = "alipay.eco.mycar.parking.order.pay"
    
    	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.trade.order.query
    	AlipayEcoMyCarTradeOrderQuery = "alipay.eco.mycar.trade.order.query"
    
    	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.agreement.query
    	AlipayEcoMyCarParkingAgreement = "alipay.eco.mycar.parking.agreement.query"
    
    	// https://opendocs.alipay.com/apis/api_9/alipay.user.info.auth
    	AlipayUserInfoAuth = "alipay.user.info.auth"
    
    	// https://opendocs.alipay.com/apis/api_9/alipay.system.oauth.token
    	AlipaySystemOauthToken = "alipay.system.oauth.token"
    
    	// https://opendocs.alipay.com/apis/api_9/alipay.open.auth.token.app
    	AlipayOpenAuthTokenApp = "alipay.open.auth.token.app"
    
    	// https://opendocs.alipay.com/apis/api_1/alipay.trade.page.pay
    	AlipayTradePagePay = "alipay.trade.page.pay"
    
    	// https://opendocs.alipay.com/apis/api_1/alipay.trade.app.pay
    	AlipayTradeAppPay = "alipay.trade.app.pay"
    
    	// https://opendocs.alipay.com/apis/api_1/alipay.trade.wap.pay
    	AlipayTradeWapPay = "alipay.trade.wap.pay"
    
    	// https://opendocs.alipay.com/apis/api_2/alipay.user.certify.open.certify
    	AlipayUserCertifyOpenCertify = "alipay.user.certify.open.certify"
    
    	// https://opendocs.alipay.com/apis/api_1/alipay.trade.fastpay.refund.query
    	AlipayTradeFastpayRefundQuery = "alipay.trade.fastpay.refund.query"
    
    	// https://opendocs.alipay.com/apis/api_1/alipay.trade.order.settle
    	AlipayTradeOrderSettle = "alipay.trade.order.settle"
    
    	// https://opendocs.alipay.com/apis/api_1/alipay.trade.create
    	AlipayTradeCreate = "alipay.trade.create"
    
    	// https://opendocs.alipay.com/apis/api_1/alipay.trade.close
    	AlipayTradeClose = "alipay.trade.close"
    
    	// https://opendocs.alipay.com/apis/api_1/alipay.trade.cancel
    	AlipayTradeCancel = "alipay.trade.cancel"
    
    	// https://opendocs.alipay.com/apis/api_1/alipay.trade.refund
    	AlipayTradeRefund = "alipay.trade.refund"
    
    	// https://opendocs.alipay.com/apis/api_1/alipay.page.trade.refund
    	AlipayTradePageRefund = "alipay.trade.page.refund"
    
    	// https://opendocs.alipay.com/apis/api_1/alipay.trade.precreate
    	AlipayTradePrecreate = "alipay.trade.precreate"
    
    	// https://opendocs.alipay.com/apis/api_1/alipay.trade.query
    	AlipayTradeQuery = "alipay.trade.query"
    
    	// https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.toaccount.transfer
    	AlipayFundTransToAccountTransfer = "alipay.fund.trans.toaccount.transfer"
    
    	// https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.uni.transfer
    	AlipayFundTransUniTransfer = "alipay.fund.trans.uni.transfer"
    
    	// https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.common.query
    	AlipayFundTransCommonQuery = "alipay.fund.trans.common.query"
    
    	// https://opendocs.alipay.com/apis/api_28/alipay.fund.account.query
    	AlipayFundAccountQuery = "alipay.fund.account.query"
    
    	// https://opendocs.alipay.com/apis/api_2/alipay.user.info.share
    	AlipayUserInfoShare = "alipay.user.info.share"
    
    	// https://opendocs.alipay.com/apis/api_8/zhima.credit.score.get
    	ZhimaCreditScoreGet = "zhima.credit.score.get"
    
    	// https://opendocs.alipay.com/apis/api_2/alipay.user.certify.open.initialize
    	AlipayUserCertifyOpenInitialize = "alipay.user.certify.open.initialize"
    
    	// https://opendocs.alipay.com/apis/api_2/alipay.user.certify.open.query
    	AlipayUserCertifyOpenQuery = "alipay.user.certify.open.query"
    

    You can use Success() func of these structs to check if the response return successfully

        ...
        var resp AlipayTradeCreate
        ...
        if resp.Success() {
        	....
        }   

    Inspirited

    Visit original content creator repository https://github.com/WenyXu/better-alipay-go
  • SwaggerLume

    Total Downloads
    Build Status
    Coverage Status
    Code Climate
    StyleCI

    SwaggerLume

    Swagger 2.0-3.0 for Lumen

    This package is a wrapper of Swagger-php and swagger-ui adapted to work with Lumen.

    Installation

    Lumen Swagger UI OpenAPI Spec compatibility L5-Swagger
    5.0 – 5.3 2.2 1.1, 1.2, 2.0 composer require "darkaonline/swagger-lume:~1.0"
    5.4.x 2.2 1.1, 1.2, 2.0 composer require "darkaonline/swagger-lume:~2.0"
    5.4.x 3 2.0 composer require "darkaonline/swagger-lume:~3.0"
    5.5.x 3 2.0 composer require "darkaonline/swagger-lume:5.5.*"
    5.6 – 5.7 3 2.0, 3.0 composer require "darkaonline/swagger-lume:5.6.*"
    6.0 3 2.0, 3.0 composer require "darkaonline/swagger-lume:6.*"
    7.0 3 2.0, 3.0 composer require "darkaonline/swagger-lume:7.*"
    8.0 3 2.0, 3.0 composer require "darkaonline/swagger-lume:8.*"
    9.0 3 2.0, 3.0 composer require "darkaonline/swagger-lume:9.*"
    10.0 3 2.0, 3.0 composer require "darkaonline/swagger-lume:10.*"

    • Open your bootstrap/app.php file and:

    uncomment this line (around line 26) in Create The Application section:

         $app->withFacades();

    add this line before Register Container Bindings section:

         $app->configure('swagger-lume');

    add this line in Register Service Providers section:

        $app->register(\SwaggerLume\ServiceProvider::class);
    • Run php artisan swagger-lume:publish-config to publish configs (config/swagger-lume.php)
    • Make configuration changes if needed
    • Run php artisan swagger-lume:publish to publish everything

    If you would like to use latest OpenApi specifications (originally known as the Swagger Specification) in your project you should:

    • Explicitly require swagger-php version 3.* in your projects composer by running:
    composer require 'zircote/swagger-php:3.*'
    • Set environment variable SWAGGER_VERSION to 3.0 in your .env file:
    SWAGGER_VERSION=3.0
    

    or in your config/l5-swagger.php:

    'swagger_version' => env('SWAGGER_VERSION', '3.0'),

    Configuration

    • Run php artisan swagger-lume:publish-config to publish configs (config/swagger-lume.php)
    • Run php artisan swagger-lume:publish-views to publish views (resources/views/vendor/swagger-lume)
    • Run php artisan swagger-lume:publish to publish everything
    • Run php artisan swagger-lume:generate to generate docs

    Changes in 3.0

    Changes in 2.0

    • Lumen 5.4 support
    • Swagger UI 2.2.8

    Migrate from 2.0 to 3.0 or 5.5

    • Remove config/swagger-lume.php file (make a copy if needed)
    • Remove public/vendor/swagger-lume directory
    • Remove resources/views/vendor/swagger-lume directory
    • Run swagger-lume:publish to publish new swagger-ui view and configuration
    • Edit your config/swagger-lume.php file

    Swagger-php

    The actual Swagger spec is beyond the scope of this package. All SwaggerLume does is package up swagger-php and swagger-ui in a Laravel-friendly fashion, and tries to make it easy to serve. For info on how to use swagger-php look here. For good examples of swagger-php in action look here.

    Important: Make sure that you add the include statement on all pages with annotations!

    use OpenApi\Annotations as OA;
    

    Support on Beerpay

    Hey dude! Help me out for a couple of 🍻!

    Beerpay Beerpay

    Visit original content creator repository
    https://github.com/DarkaOnLine/SwaggerLume

  • SwaggerLume

    Total Downloads Build Status Coverage Status Code Climate StyleCI

    SwaggerLume

    Swagger 2.0-3.0 for Lumen

    This package is a wrapper of Swagger-php and swagger-ui adapted to work with Lumen.

    Installation

    Lumen Swagger UI OpenAPI Spec compatibility L5-Swagger
    5.0 – 5.3 2.2 1.1, 1.2, 2.0 composer require "darkaonline/swagger-lume:~1.0"
    5.4.x 2.2 1.1, 1.2, 2.0 composer require "darkaonline/swagger-lume:~2.0"
    5.4.x 3 2.0 composer require "darkaonline/swagger-lume:~3.0"
    5.5.x 3 2.0 composer require "darkaonline/swagger-lume:5.5.*"
    5.6 – 5.7 3 2.0, 3.0 composer require "darkaonline/swagger-lume:5.6.*"
    6.0 3 2.0, 3.0 composer require "darkaonline/swagger-lume:6.*"
    7.0 3 2.0, 3.0 composer require "darkaonline/swagger-lume:7.*"
    8.0 3 2.0, 3.0 composer require "darkaonline/swagger-lume:8.*"
    9.0 3 2.0, 3.0 composer require "darkaonline/swagger-lume:9.*"
    10.0 3 2.0, 3.0 composer require "darkaonline/swagger-lume:10.*"
    • Open your bootstrap/app.php file and:

    uncomment this line (around line 26) in Create The Application section:

         $app->withFacades();

    add this line before Register Container Bindings section:

         $app->configure('swagger-lume');

    add this line in Register Service Providers section:

        $app->register(\SwaggerLume\ServiceProvider::class);
    • Run php artisan swagger-lume:publish-config to publish configs (config/swagger-lume.php)
    • Make configuration changes if needed
    • Run php artisan swagger-lume:publish to publish everything

    If you would like to use latest OpenApi specifications (originally known as the Swagger Specification) in your project you should:

    • Explicitly require swagger-php version 3.* in your projects composer by running:
    composer require 'zircote/swagger-php:3.*'
    • Set environment variable SWAGGER_VERSION to 3.0 in your .env file:
    SWAGGER_VERSION=3.0
    

    or in your config/l5-swagger.php:

    'swagger_version' => env('SWAGGER_VERSION', '3.0'),

    Configuration

    • Run php artisan swagger-lume:publish-config to publish configs (config/swagger-lume.php)
    • Run php artisan swagger-lume:publish-views to publish views (resources/views/vendor/swagger-lume)
    • Run php artisan swagger-lume:publish to publish everything
    • Run php artisan swagger-lume:generate to generate docs

    Changes in 3.0

    Changes in 2.0

    • Lumen 5.4 support
    • Swagger UI 2.2.8

    Migrate from 2.0 to 3.0 or 5.5

    • Remove config/swagger-lume.php file (make a copy if needed)
    • Remove public/vendor/swagger-lume directory
    • Remove resources/views/vendor/swagger-lume directory
    • Run swagger-lume:publish to publish new swagger-ui view and configuration
    • Edit your config/swagger-lume.php file

    Swagger-php

    The actual Swagger spec is beyond the scope of this package. All SwaggerLume does is package up swagger-php and swagger-ui in a Laravel-friendly fashion, and tries to make it easy to serve. For info on how to use swagger-php look here. For good examples of swagger-php in action look here.

    Important: Make sure that you add the include statement on all pages with annotations!

    use OpenApi\Annotations as OA;
    

    Support on Beerpay

    Hey dude! Help me out for a couple of 🍻!

    Beerpay Beerpay

    Visit original content creator repository https://github.com/DarkaOnLine/SwaggerLume