preface

First, a few important concepts emerge from this article:

Function Compute: Function Compute is an event-driven service that lets the user write code and upload it instead of managing the server. Function computing prepares computing resources and runs user code on an elastic scale, with users paying only for the resources consumed by the actual code running. Function calculation refer to more information. Funcraft: Funcraft is a tool used to support Serverless application deployment. Funcraft helps you easily manage functions, API gateways, logging services, and other resources. It helps you develop, build, and deploy through a resource configuration file (template.yml). More documentation references for Fun. OSS: object storage. Massive, secure, low-cost and highly reliable cloud storage services provide 99.9999999999% data reliability. RESTful apis are used to store and access data in any location on the Internet. Capacity and processing capabilities are flexibly expanded, and storage costs are optimized for multiple storage types. ROS: Resource Choreography (ROS) is an easy-to-use cloud computing resource management and automated operation and maintenance service. Users can use templates to describe the dependencies and configurations of multiple cloud computing resources, and automatically create and configure all resources for automatic deployment, operation and maintenance (O&M). Choreography templates are also a standardized way of delivering resources and applications that can be edited at any time, enabling Infrastructure as Code. CI/CD: CI/CD is a method of frequently delivering applications to customers by introducing automation in the application development phase. The core concepts of CI/CD are continuous integration, continuous delivery, and continuous deployment.

The target

This article intends to use a simple functional calculation project as an example to write test cases and configure them to support CI/CD workflows. Achieve the following four mini-goals:

  1. The CI is triggered by a Git commit
  2. Perform tests (unit, integration, and end-to-end)
  3. Functions are packaged and uploaded to OSS
  4. ROS deployment functions to the Staging environment

Work flow chart

The familiar Github repository is used here, in conjunction with Travis CI. When a user pushes or PR (Pull Request) on the sample project, the Travis CI is automatically triggered for unit testing, build packaging, and deployment publishing.

The sample project

The example project address is: github.com/vangie/tz-t… , the project is a simple Web function based on FC Http trigger, which returns the current time in a specified time zone. The project directory structure is as follows

Tz - time ├ ─ ─. Funignore ├ ─ ─. Travis. Yml ├ ─ ─ a Makefile ├ ─ ─ bin │ ├ ─ ─ delRosStack. Sh │ ├ ─ ─ deployE2EStack. Sh │ └ ─ ─wait├─ Index. Integration.js ├─ Index.js ├─ Index.js ├─ index.js ├─ Jest. Config. E2e. Js ├ ─ ─ jest. Config. Integration. Js ├ ─ ─ package - lock. Json ├ ─ ─ package. The json └ ─ ─ the template. The ymlCopy the code

Functions of some documents:

  • .funignore– Funcraft Sudden file list during deployment
  • .travis.yml– Travis CI configuration file
  • index.js– Function entry file
  • *.test.js – Unit test related files
  • *.integraion-test.js – Integration test related files
  • *.e23-test.js – End-to-end test related files
  • Template.yml-ros description file used to describe functions and other cloud services

Automated testing

Tests usually fall into three categories: unit tests, integration tests, and E2E tests. In a functional computing scenario, these three types of tests can be implemented as follows.

  • Unit testing – Use Mock classes to test functions and validate input and output parameters
  • Integration testing – usefun local invoke/startAnalog running function
  • E2E testing – Deploy a test environment using Fun Deploy and then make mock calls via Fun Invoke or send them directly via CURL

This example implements only unit tests, integration tests and E2E tests trigger in a similar way to the Travis example, and the implementation can be configured by referring to the method tips above.

Unit testing

Unit tests of FC functions are no different from normal functions. Use a familiar unit testing framework, which in this case uses JEST. Let’s look at a test case snippet

jest.mock('moment-timezone');

const { tz } = require('moment-timezone');
const { handler } = require('./index');

const EXPECTED_DATE = '2018-10-01 00:00:00';
const TIMEZONE = 'America/New_York';

describe('when call handle', () => {
    it('Should return the expected date if the provied timezone exists', () = > {const mockReq = {
            queries: {
                tz: TIMEZONE
            }
        }
        const mockResp = {
            setHeader: jest.fn(),
            send: jest.fn()
        }

        tz.names = (a)= > [TIMEZONE];
        tz.mockImplementation((a)= > {
            return {
                format: (a)= > EXPECTED_DATE 
            }
        })

        handler(mockReq, mockResp, null);

        expect(mockResp.setHeader.mock.calls.length).toBe(1);
        expect(mockResp.setHeader.mock.calls[0] [0]).toBe('content-type');
        expect(mockResp.setHeader.mock.calls[0] [1]).toBe('application/json');

        expect(mockResp.send.mock.calls.length).toBe(1);
        expect(mockResp.send.mock.calls[0] [0]).toBe(JSON.stringify({
            statusCode: '200'.message: `The time in ${TIMEZONE} is: ${EXPECTED_DATE}`
        }, null.' '));

    });
});
Copy the code

Mock the moment-timezone with jest. Mock so that tz returns a predefined value when called instead of a dynamically changing time.

Typically, this type of unit test is divided into three steps:

  1. Mock dependent values or parameters
  2. Calling the test function
  3. An assertion returns the result and the argument to be called

If there are no native dependencies (dependencies on Linux executables or so libraries), use NPM test to trigger the test. If there are native dependencies, run the following command in fun’s sbox simulation to trigger the test

fun install sbox -f tz-time --cmd 'npm install'
Copy the code

Integration testing

The integration test in this example starts the function locally with the fun local start command. Since the function is configured with an HTTP trigger, the function can be invoked via an HTTP request.

Integration tests are still written in the Jest framework. To distinguish the integration test file from the unit test file *.test.js, the integration test file uses the.integration-test.js file suffix. To make jest command run independent integration test cases rather than and mixed together, the unit test to compile the following file jest. Config. Integration. Js

module.exports = {
    testMatch: ["* * /? (*) integration-test.js"]};Copy the code

Then configure scripts in package.json

"scripts": {
    "integration:test": "jest -c jest.config.integration.js"
}
Copy the code

You can then perform integration tests by executing NPM Run Integration :test.

Then add the integration-test target to the Makefile based on this:

funlocal.PID:fun local start & echo ? ! >$@

integration-test: funlocal.PID 
	bin/waitForServer.sh http://localhost:8000/2016-08-15/proxy/tz-time/tz-time/
	npm run integration:test
	kill -2 `cat $<` && rm $<
Copy the code

The integration-test target relies on the funlocal.PID target, which is responsible for starting a Fun Local process that starts port 8000 locally. Read the Makefile code above

  • fun local start & echo ? ! > $@Start the fun Local process and write the process PID to the target file of the same name funlocal.pid
  • bin/waitForServer.sh http://localhost:8000/2016-08-15/proxy/tz-time/tz-time/Test whether the Fun Local process has started with a URL.
  • kill -2 `cat $<` && rm $<Destroy the Fun Local process after the test is complete.

NPM Run Integration :test launches several test cases, one of which is shown in the following example:

const request = require('request');

const url = 'http://localhost:8000/2016-08-15/proxy/tz-time/tz-time/';

describe('request url', () => {
    it('without tz', (done) => {
        request(url, (error, response, data) => {
            if (error) {
                fail(error);
            } else {
                const resData = JSON.parse(data);
                expect(resData.statusCode).toBe(200);
                expect(resData.message).toContain('Asia/Shanghai');
            }
            done();
        });
    });
});
Copy the code

End-to-end testing

The test cases for end-to-end testing and integration testing are very similar, but the difference lies in the server side of testing, where end-to-end testing deploys a real environment and integration testing is locally simulated by Fun Local.

In this example, use fun deploy-use-ros to deploy an environment named TZ-e2e – with a timestamp, so that each test deploys a new environment without affecting each other. After the test is complete, delete the ROS stack using aliyun-CLI tool.

The following Makefile target is tested end-to-end:

stack_name := tz-e2e-$(shell date +%s)

e2e-test:
	# deploy e2e 
	bin/deployE2EStack.sh $(stack_name)
	# run test
	npm run e2e:test
	# cleanup
	bin/delRosStack.sh $(stack_name)
Copy the code
  • bin/deployE2EStack.sh $(stack_name)Responsible for deploying a new ROS Stack. You need to build the deliverables using the Fun Package before deploying, and refer to the next section for details on how to build the deliverables.
  • npm run e2e:testRun end-to-end tests
  • bin/delRosStack.sh $(stack_name)After the test is complete, cleaning up the deployed ROS Stack frees up responding cloud resources.

Build deliverables

The fun package command can be used to build the deliverables, and the Fun package needs to specify an OSS bucket. The fun package command completes the following steps:

  1. Package the code compilation into a Zip file.
  2. Upload the code package to the OSS bucket.
  3. Generate a new file, template.packaged. Yml, with the code local address changed to the OSS bucket address.

The generated template.packaged. Yml file is the final deliverable and can be deployed with the Fun Deploy name.

Continuous deployment

Once the build has produced the deliverables, it can be deployed with Fun Deploy. Continuous deployment needs to address two issues:

  1. New deployment and upgrade deployment are supported
  2. One set of resource descriptions supports the deployment of multiple sets, such as test, staging, and Production environments.

Fun Deploy can easily solve these problems with ROS.

fun deploy --use-ros --stack-name tz-staging --assume-yes
Copy the code

Among them:

  • --use-rosRepresents deployment with ROS, which works by pushing template.yml to the ROS service, which creates and updates each service. Without this parameter, Fun parses template.yml locally, calling the API for resource creation. ROS has the added benefit of being able to roll back deployment, automatically in the event of a failure.
  • --stack-nameSpecify the name of a stack, a ROS concept that can be understood as a set of environments.
  • --assume-yesUsed in unattended mode, skip the confirmation prompt.

Note that if the –use-ros parameter is not specified, fun Deploy will be deployed directly by calling the cloud resource API. This is the default deployment mode of Fun Deploy, although idempotent deployment is also implemented. However, only limited cloud resources (FC, OTS, API Gateway, etc.) can be deployed, which is far less abundant than ROS, and ROS does not support rollback and one-click delete. Therefore, it is not recommended here.

summary

For the scripted configuration of all the above steps, refer to the Makefile and.travis. Yml files. Github and Travis CI can be linked through the above two files to achieve CI/CD triggered by code submission.

This article describes FC functions

  1. How do you test, especially the automation of unit tests
  2. How do I build the deliverables by uploading the code file to the OSS Bucket via fun Package and having the deliverables program a text description file template.packaged. Yml
  3. How to continuously deploy, using ROS to deploy multiple environments and update the same environment multiple times.

Refer to the reading

  1. Source code example project TZ-time
  2. Develop proper posture for function computations – use ROS for resource orchestration
  3. Funcraft
  4. Aliyun Serverless VSCode Extension

“Alibaba Cloud originators pay close attention to technical fields such as microservice, Serverless, container and Service Mesh, focus on cloud native popular technology trends and large-scale implementation of cloud native, and become the technical circle that knows most about cloud native developers.”