Skip to content

Commit

Permalink
Enhance user experience (#57)
Browse files Browse the repository at this point in the history
* fix ots table regex

* fix apigateway openIdConnectConfig

* add apigateway description support

* add support for api gateway auth type openid

* fix apigateway open api type typo

* add retry logic for sls and fc

* create sls logstore default index if not exist.

* fix spec document link

* reduce package code size

* add user-agent for fc client

* add more zip ignored folder or file

* add pkg config

* fix generated api gateway role

* red color for error message
  • Loading branch information
tanhe123 authored Jul 9, 2018
1 parent 205bbfc commit f8dea2e
Show file tree
Hide file tree
Showing 11 changed files with 257 additions and 58 deletions.
1 change: 1 addition & 0 deletions README-zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ https://github.com/aliyun/fun/tree/master/examples
- [以函数计算作为 API 网关后端服务](https://help.aliyun.com/document_detail/54788.html)
- [函数计算](https://www.aliyun.com/product/fc)
- [API Gateway](https://www.aliyun.com/product/apigateway)
- [Fun 发布 2.0 新版本啦](https://yq.aliyun.com/articles/604490)

## 开源许可

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ You can find more complicated examples here:
- [以函数计算作为 API 网关后端服务](https://help.aliyun.com/document_detail/54788.html)
- [函数计算](https://www.aliyun.com/product/fc)
- [API Gateway](https://www.aliyun.com/product/apigateway)
- [Fun 发布 2.0 新版本啦](https://yq.aliyun.com/articles/604490)

## License

Expand Down
4 changes: 2 additions & 2 deletions docs/specs/2018-04-03-zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ Resources:
Role | `string` | 使用一个 RAM 角色的 ARN 为函数指定执行角色。 如果忽略,将为函数创建一个默认的角色。
Policies | `string` <span>&#124;</span> `string` 列表 <span>&#124;</span> [RAM policy 文档对象](https://help.aliyun.com/document_detail/28663.html) <span>&#124;</span> [RAM policy 文档对象](https://help.aliyun.com/document_detail/28663.html) 列表 | 函数需要的阿里云管理的 RAM policies 或 RAM policy 文档的名称,将会被附加到该函数的默认角色上。如果设置了 Role 属性,则该属性会被忽略。
InternetAccess | `boolean` | 表示此服务是否可以访问公网。
VpcConfig | [Vpc 配置对象](#Vpc-配置对象) | 允许函数访问 vpc 内的服务。
LogConfig | [Log 配置对象](#Log-配置对象) | 允许函数执行的日志存储在日志服务中。
VpcConfig | [Vpc 配置对象](#vpc-配置对象) | 允许函数访问 vpc 内的服务。
LogConfig | [Log 配置对象](#log-配置对象) | 允许函数执行的日志存储在日志服务中。
Description | `string` | 服务的描述。

##### Aliyun::Serverless::Function
Expand Down
2 changes: 1 addition & 1 deletion examples/openid_connect/template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Resources:
parameterType: 'REQUIRED'
x-aliyun-apigateway-visibility: PRIVATE
x-aliyun-apigateway-auth-type: 'APPOPENID'
x-aliyun-apigateway-openid-connect-config:
x-aliyun-apigateway-open-id-connect-config:
idTokenParamName: 'token'
openidApiType: 'BUSINESS'
x-aliyun-apigateway-fc:
Expand Down
36 changes: 28 additions & 8 deletions lib/deploy/deploy-by-tpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ const readFile = util.promisify(fs.readFile);

let {
makeApi, makeApiTrigger, makeFunction,
makeGroup, makeOtsTable, makeOtsInstance, makeOtsTrigger, makeService, makeTrigger, makeSlsProject, makeLogstore
makeGroup, makeOtsTable, makeOtsInstance,
makeOtsTrigger, makeService, makeTrigger,
makeSlsProject, makeLogstore, makeLogstoreIndex
} = require('./deploy-support');

let {
Expand Down Expand Up @@ -181,34 +183,46 @@ async function deployFcService(serviceName, serviceDefinition) {
await deployFunctions(serviceName, serviceDefinition);
}

async function deployLogstoreDefaultIndex(projectName, logstoreName) {
await makeLogstoreIndex(projectName, logstoreName);
}

async function deployLogstore(projectName, logstoreDefinition) {
for (const [logstoreName, v] of Object.entries(logstoreDefinition)) {
if ((v || {}).Type === 'Aliyun::Serverless::Log::Logstore') {
const properties = (v || {}).Properties;
const ttl = properties.TTL;
const shardCount = properties.ShardCount;

console.log(`\tWaiting for log service logstore ${logstoreName} to be deployed...`);

await makeLogstore({
projectName,
logstoreName,
ttl,
shardCount
});

await deployLogstoreDefaultIndex(projectName, logstoreName);

console.log(green(`\tlog serivce logstore ${logstoreName} deploy success` ));
}
}
}



async function deployLogs(resourcesDefinition) {
for (const [projectName, v] of Object.entries(resourcesDefinition)) {
if ((v || {}).Type === 'Aliyun::Serverless::Log') {
console.log(`Waiting for log ${projectName} to be deployed...`);
console.log(`Waiting for log service project ${projectName} to be deployed...`);
const properties = (v || {}).Properties;

const description = properties.Description || '';
await makeSlsProject(projectName, description);

await deployLogstore(projectName, v);
console.log(green(`log ${projectName} deploy success\n` ));
console.log(green(`log serivce project ${projectName} deploy success\n` ));
}
}
}
Expand Down Expand Up @@ -298,10 +312,10 @@ async function deployApigateway(name, { apiDefinition, template, tplPath }) {
if ( fcDefinition.role ) {
roleName = extractFcRole(fcDefinition.role);
} else {
roleName = `aliyunapigatewayaccessingfcrole`;
roleName = `AliyunFcGeneratedApiGatewayRole`;
}

const role = await makeRole(roleName, true, 'API Gateway access FunctionCompute', JSON.stringify({
const role = await makeRole(roleName, true, 'API Gateway access FunctionCompute', {
'Statement': [
{
'Action': 'sts:AssumeRole',
Expand All @@ -314,7 +328,7 @@ async function deployApigateway(name, { apiDefinition, template, tplPath }) {
}
],
'Version': '1'
}));
});

const policyName = 'AliyunFCInvocationAccess';
await attachPolicyToRole(policyName, roleName);
Expand All @@ -335,6 +349,11 @@ async function deployApigateway(name, { apiDefinition, template, tplPath }) {

const requestConfig = methodDefinition['x-aliyun-apigateway-request-config'] || {};

let openIdConnectConfig = methodDefinition['x-aliyun-apigateway-open-id-connect-config'];
if (!openIdConnectConfig) {
openIdConnectConfig = methodDefinition['x-aliyun-apigateway-openid-connect-config'];
}

await makeApi(apiGroup, {
stageName: apiDefinition.Properties.StageName,
requestPath: k,
Expand All @@ -347,11 +366,12 @@ async function deployApigateway(name, { apiDefinition, template, tplPath }) {
parameters: methodDefinition['x-aliyun-apigateway-request-parameters'],
auth: {
type: methodDefinition['x-aliyun-apigateway-auth-type'],
config: methodDefinition['x-aliyun-apigateway-openid-connect-config'],
config: openIdConnectConfig,
},
visibility: methodDefinition['x-aliyun-apigateway-visibility'],
requestConfig: requestConfig,
resultConfig: resultConfig
resultConfig: resultConfig,
description: methodDefinition['x-aliyun-apigateway-description']
});
}
}
Expand Down
124 changes: 96 additions & 28 deletions lib/deploy/deploy-support.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,24 @@ const fs = require('fs');
const util = require('util');

const Log = require('@alicloud/log');
const FC = require('@alicloud/fc');
const FC = require('@alicloud/fc2');
const ots = require('@alicloud/ots2');
const Pop = require('@alicloud/pop-core');
const CloudAPI = require('@alicloud/cloudapi');

const getProfile = require('../profile').getProfile;
const pkg = require('../../package.json');
const zip = require('../zip');
const debug = require('debug')('fun:deploy');
const osLocale = require('os-locale');

const promiseRetry = require('promise-retry');
const retryOptions = {
retries: 6,
factor: 2,
minTimeout: 1 * 1000,
randomize: true
};

let {
makeRole, attachPolicyToRole, makePolicy
Expand All @@ -24,11 +34,16 @@ const { green, red } = require('colors');
const getFcClient = async () => {
const profile = await getProfile();

const locale = await osLocale();

return new FC(profile.accountId, {
accessKeyID: profile.accessKeyId,
accessKeySecret: profile.accessKeySecret,
region: profile.defaultRegion,
timeout: 60000
timeout: 60000,
headers: {
'user-agent': `${pkg.name}/v${pkg.version} ( Node.js ${process.version}; OS ${process.platform} ${process.arch}; language ${locale} )`
}
});
};

Expand Down Expand Up @@ -77,6 +92,40 @@ const getSlsClient = async () => {
});
};

async function makeLogstoreIndex(projectName, logstoreName) {
const sls = await getSlsClient();

try {
await sls.getIndexConfig(projectName, logstoreName);
return ;
} catch (ex) {
if (ex.code !== 'IndexConfigNotExist') {
throw ex;
}
}

// create index if index not exist.
console.log(`\t\tWaiting for log service logstore ${logstoreName} default index to be deployed...`);
await promiseRetry(async (retry, times) => {
try {
// create default logstore index. index configuration is same with sls console.
await sls.createIndex(projectName, logstoreName, {
ttl: 10,
line: {
caseSensitive: false,
chn: false,
token: [...', \'";=()[]{}?@&<>/:\n\t\r']
}
});
} catch (ex) {
console.log(red(`\t\t\tretry ${times} times`));
retry(ex);
}
}, retryOptions);

console.log(green(`\t\tlog service logstore ${logstoreName} default index deploy success`));
}

async function makeLogstore({
projectName,
logstoreName,
Expand All @@ -86,20 +135,32 @@ async function makeLogstore({
const sls = await getSlsClient();

let exists = true;
await promiseRetry(async (retry, times) => {
try {
await sls.getLogStore(projectName, logstoreName);
} catch (ex) {
if (ex.code === 'InternalServerError') {
console.log(red(`\t\tretry ${times} times`));

try {
await sls.getLogStore(projectName, logstoreName);
} catch (ex) {
if (ex.code !== 'LogStoreNotExist') {
throw ex;
} else {exists = false;}
}
retry(ex);
} else if (ex.code !== 'LogStoreNotExist') {
throw ex;
} else {exists = false;}
}
}, retryOptions);

if (!exists) {
await sls.createLogStore(projectName, logstoreName, {
ttl,
shardCount
});
await promiseRetry(async (retry, times) => {
try {
await sls.createLogStore(projectName, logstoreName, {
ttl,
shardCount
});
} catch (ex) {
console.log(red(`\t\tretry ${times} times`));
retry(ex);
}
}, retryOptions);
} else {
try {
await sls.updateLogStore(projectName, logstoreName, {
Expand All @@ -112,8 +173,6 @@ async function makeLogstore({
}
}
}


}

async function makeSlsProject(projectName, description) {
Expand Down Expand Up @@ -143,7 +202,7 @@ async function makeSlsProject(projectName, description) {
});
} catch (ex) {
if (ex.code === 'ProjectAlreadyExist') {
console.error(`error: sls project ${projectName} already exist, it may be in other region or created by other users.`);
console.error(red(`error: sls project ${projectName} already exist, it may be in other region or created by other users.`));
process.exit(-1);
} else {
throw ex;
Expand Down Expand Up @@ -191,11 +250,18 @@ async function makeService({serviceName,
});
}

if (!service) {
service = await fc.createService(serviceName, options);
} else {
service = await fc.updateService(serviceName, options);
}
await promiseRetry(async (retry, times) => {
try {
if (!service) {
service = await fc.createService(serviceName, options);
} else {
service = await fc.updateService(serviceName, options);
}
} catch (ex) {
console.log(red(`\tretry ${times} times`));
retry(ex);
}
}, retryOptions);

return service;
}
Expand Down Expand Up @@ -494,7 +560,8 @@ async function makeApi(group, {
auth = {},
visibility = 'Private',
requestConfig = {},
resultConfig = {}
resultConfig = {},
description
}) {
const ag = await getCloudApiClient();

Expand Down Expand Up @@ -535,7 +602,7 @@ async function makeApi(group, {
GroupId: group.GroupId,
ApiName: apiName,
Visibility: visibility,
Description: 'The awesome api',
Description: description || 'The awesome api generated by fun',
AuthType: auth.type || 'ANONYMOUS',
RequestConfig: JSON.stringify(mergedRequestConfig),
RequestParameters: JSON.stringify(requestParameters),
Expand Down Expand Up @@ -564,11 +631,11 @@ async function makeApi(group, {
FailResultSample: resultConfig.failResultSample || 'failed samples'
};

if (auth.type === 'APPOPENID') {
if (auth.type === 'APPOPENID' || auth.type === 'OPENID') {
var openidConf = auth.config || {};
params.OpenIdConnectConfig = JSON.stringify({
'IdTokenParamName': openidConf.idTokenParamName || 'token',
'OpenIdApiType': openidConf.openidApiType || 'BUSINESS',
'IdTokenParamName': openidConf.idTokenParamName,
'OpenIdApiType': openidConf.openIdApiType || 'BUSINESS',
'PublicKeyId': openidConf.publicKeyId,
'PublicKey': openidConf.publicKey
});
Expand Down Expand Up @@ -711,6 +778,7 @@ async function makeOtsTable({

module.exports = {
makeApi, makeApiTrigger, makeFunction,
makeGroup, makeOtsTable, makeOtsInstance, makeOtsTrigger,
makeService, makeTrigger, makeSlsProject, makeLogstore
makeGroup, makeOtsTable, makeOtsInstance,
makeOtsTrigger, makeService, makeTrigger, makeSlsProject,
makeLogstore, makeLogstoreIndex
};
2 changes: 1 addition & 1 deletion lib/validate/schema/ots-resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const otsResourceSchema = {
},
},
'patternProperties': {
'^(?!Type|Properties)[a-zA-Z][a-zA-Z0-9-]{0,127}$': {
'^(?!Type|Properties)[a-zA-Z][a-zA-Z0-9_]{0,127}$': {
'$ref': '/Resources/TableStore/Table'
}
},
Expand Down
10 changes: 9 additions & 1 deletion lib/zip.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const lstat = util.promisify(fs.lstat);

const buildDeps = require('./deps');
const readlink = util.promisify(fs.readlink);
const zipIgnored = ['.git', '.svn', '.env'];

function globAsync(pattern, cwd) {
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -80,6 +81,10 @@ async function zipFolder(zip, folder, folders) {
const fPath = path.join(dir, f);
const s = await lstat(fPath);

if (zipIgnored.includes(f)) {
return ;
}

if (s.isFile()) {
zip.file(f, fs.createReadStream(fPath), {
unixPermissions: permStr(s.mode)
Expand Down Expand Up @@ -116,6 +121,9 @@ exports.file = async function (file) {
return zip.generateAsync({
type: 'base64',
platform:'UNIX',
compression: 'DEFLATE'
compression: 'DEFLATE',
compressionOptions: {
level: 9
}
});
};
Loading

0 comments on commit f8dea2e

Please sign in to comment.