From fbfd046f65a06023678653c65d279d140bbcf5ef Mon Sep 17 00:00:00 2001 From: tanhe123 Date: Wed, 9 Jan 2019 17:17:46 +0800 Subject: [PATCH] add nas file manager application demo --- .gitignore | 3 + README-zh.md | 1 + README.md | 1 + docs/usage/getting_started-zh.md | 2 +- examples/nas_browser/README.md | 1 + examples/nas_browser/nas.sh | 144 +++++++++++++++++++++++++++ examples/nas_browser/src/index.py | 155 ++++++++++++++++++++++++++++++ examples/nas_browser/template.yml | 37 +++++++ 8 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 examples/nas_browser/README.md create mode 100755 examples/nas_browser/nas.sh create mode 100644 examples/nas_browser/src/index.py create mode 100644 examples/nas_browser/template.yml diff --git a/.gitignore b/.gitignore index 0c95702dd..342cf6aa5 100644 --- a/.gitignore +++ b/.gitignore @@ -75,6 +75,9 @@ examples/java/demo/target/ examples/local/java8/target/* !examples/local/java8/target/demo-1.0-SNAPSHOT.jar +examples/nas_browser/src/* +!examples/nas_browser/src/index.py + .idea/ .vscode/ diff --git a/README-zh.md b/README-zh.md index e0a64a3e6..1098a5d40 100644 --- a/README-zh.md +++ b/README-zh.md @@ -28,6 +28,7 @@ Fun 作为一个命令行工具,内置了多个子命令,比如 config、loc - [开发函数计算的正确姿势 —— 本地运行、调试、发布 NAS 函数](https://yq.aliyun.com/articles/683684): 介绍了如何在本地运行、单步调试配置了 NAS 服务的函数。 - [开发函数计算的正确姿势 —— Api 本地运行调试](https://yq.aliyun.com/articles/683685): 介绍了如何在通过 API 在本地运行、单步调试函数。 - [开发函数计算的正确姿势 —— 开发 WordPress 应用](https://yq.aliyun.com/articles/683686): 通过一个实战场景,介绍了如何利用 Fun 工具本地开发 WordPress Web 应用。 + - [开发函数计算的正确姿势 —— 开发 NAS 文件管理应用](https://yq.aliyun.com/articles/685803): 介绍了如何使用 Fun Local 开发一个 NAS 文件管理 Web 应用。 - [Fun 规范文档](https://github.com/aliyun/fun/blob/master/docs/specs/2018-04-03-zh-cn.md): 详细介绍了 Fun 规范文档的细节。 - [常见问题与解答](https://github.com/aliyun/fun/blob/master/docs/usage/faq-zh.md): 使用 Fun 时的常见问题与解答。 - [更多示例](https://github.com/aliyun/fun/tree/master/examples) diff --git a/README.md b/README.md index c9d2e75a5..479340a2e 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ We have prepared a series of tutorials to help you use the Fun tool more easily: - [开发函数计算的正确姿势 —— 本地运行、调试、发布 NAS 函数](https://yq.aliyun.com/articles/683684): Demonstrates how to run and debug functions configured with NAS services locally. - [开发函数计算的正确姿势 —— Api 本地运行调试](https://yq.aliyun.com/articles/683685): Demonstrates how to run and debug functions locally through the API. - [开发函数计算的正确姿势 —— 开发 WordPress 应用](https://yq.aliyun.com/articles/683686): Demonstrates how to develop and debug a WordPress web application locally. + - [开发函数计算的正确姿势 —— 开发 NAS 文件管理应用](https://yq.aliyun.com/articles/685803): Demonstrates how to develop and debug a NAS file manager web application locally. - [Specification](https://github.com/aliyun/fun/blob/master/docs/specs/2018-04-03.md): Introduces the syntax of the fun's template.yml file. - [FAQ](https://github.com/aliyun/fun/blob/master/docs/usage/faq.md): Frequently asked questions and answers when using fun. - [More Examples](https://github.com/aliyun/fun/tree/master/examples) diff --git a/docs/usage/getting_started-zh.md b/docs/usage/getting_started-zh.md index b4b2c8d4e..d9e6044e9 100644 --- a/docs/usage/getting_started-zh.md +++ b/docs/usage/getting_started-zh.md @@ -12,7 +12,7 @@ ## 示例 -下面我们用一个简单的 helloworld 示例演示 fun 如何使用。首先在项目根目录下创建一个 hello.js 文件。 +下面我们用一个简单的 helloworld 示例演示 fun 如何使用。首先在项目根目录下创建一个 helloworld.js 文件。 ```javascript exports.handler = function(event, context, callback) { diff --git a/examples/nas_browser/README.md b/examples/nas_browser/README.md new file mode 100644 index 000000000..15dfd907f --- /dev/null +++ b/examples/nas_browser/README.md @@ -0,0 +1 @@ +# code for https://yq.aliyun.com/articles/685803 \ No newline at end of file diff --git a/examples/nas_browser/nas.sh b/examples/nas_browser/nas.sh new file mode 100755 index 000000000..5c3f6926c --- /dev/null +++ b/examples/nas_browser/nas.sh @@ -0,0 +1,144 @@ +#!/bin/bash + +set -e + +SHELL_DIR="$(dirname $0)" + +usage() { + echo "usage:" + echo -e "\t./nas.sh cp fc:" + echo -e "\t./nas.sh cp oss:/// fc:" + echo -e "\t./nas.sh ls " + echo -e "\t./nas.sh cat " + echo -e "\t./nas.sh unzip " + echo -e "\t./nas.sh bash " +} + +ENV_FILE=$(pwd)/.env + +if [ -f $ENV_FILE ]; then + . $ENV_FILE +fi + +FC_URL=${FC_URL:-http://localhost:8000/2016-08-15/proxy/nasDemo/browser/} + +checkArgs() { + if [ -z "$1" ]; then + usage + exit -1 + fi +} + +cp() { + checkArgs $1 + checkArgs $2 + + src=$1 + dst=$2 + + + if [[ "$dst" =~ ^'fc:/' ]]; then + dstFile=${dst#fc:} + else + echo "dst must start with fc:/ prefix"; + exit 1 + fi + + if [[ "$src" =~ ^'oss:' ]]; then + ossFile=$src + + curl -G -XPOST \ + $FC_URL"cp" \ + --data-urlencode "dst=$dstFile" \ + --data-urlencode "oss=$ossFile" + + exit 0 + fi + + if [[ "$src" =~ ^'/' ]]; then + absSrcFile=$src + elif [[ "$src" =~ ^'~' ]]; then + absSrcFile=$HOME${dst#~} + else + currentDir=$(pwd) + absSrcFile=$currentDir/$src + fi + + if [ ! -f "$absSrcFile" ]; then + echo "file $absSrcFile not found." + fi + + curl -XPOST \ + $FC_URL"cp?dst=$dstFile" \ + -F "file=@$absSrcFile" +} + +ls() { + dstDir=$1 + + if [[ "$dstDir" =~ ^'fc:/' ]]; then + dstDir=${dstDir#fc:} + + curl -G -XGET \ + $FC_URL"ls" \ + --data-urlencode "p=$dstDir" + else + echo "file must start with fc:/ prefix"; + exit 1 + fi +} + +cat() { + file=$1 + + if [[ "$file" =~ ^'fc:/' ]]; then + file=${file#fc:} + + curl -G -XGET \ + $FC_URL"cat" \ + --data-urlencode "file=$file" + else + echo "file must start with fc:/ prefix"; + exit 1 + fi +} + +bash() { + cmd=$(echo "$*") + checkArgs $cmd + + curl -G -XPOST \ + $FC_URL"bash" \ + --data-urlencode "cmd=$cmd" +} + +unzip() { + checkArgs $1 + zipFile=$1 + + if [[ "$zipFile" =~ ^'fc:/' ]]; then + zipFile=${zipFile#fc:} + + curl -G -XPOST \ + $FC_URL"unzip" \ + --data-urlencode "file=$zipFile" + else + echo "file must start with fc:/ prefix"; + exit 1 + fi +} + +case "$1" in + cp ) cp $2 $3;; + ls ) ls $2;; + cat ) cat $2;; + unzip ) unzip $2;; + bash ) shift; bash $@;; + -- ) shift; break ;; + "" ) usage ;; + * ) usage; exit -1 ;; +esac + + + + diff --git a/examples/nas_browser/src/index.py b/examples/nas_browser/src/index.py new file mode 100644 index 000000000..16af5c6c2 --- /dev/null +++ b/examples/nas_browser/src/index.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# coding=utf-8 +from flask import Flask +from flask import request +from flask import make_response +from flask import send_from_directory, send_file +import subprocess + +import oss2 +import sys +import os +import zipfile + +import logging + +logger = logging.getLogger() + +app = Flask(__name__) + +@app.route('/ls', methods=['GET']) +def ls(p = ''): + p = request.args.get('p', default = '', type=str) + + path = os.path.join('/', p) + + if os.path.isdir(path): + dirs = os.listdir(path) + elif os.path.isfile(path): + dirs = [os.path.basename(path)] + else: + return make_response('invliad path') + + resp = make_response('\n'.join(dirs), 200) + return resp + +@app.route('/cp', methods=['POST']) +def cp(): + + p = request.args.get('dst', default = '', type=str) + + logger.info("cp path parameter is " + p) + + path = os.path.join('/', p) + + oss_url = request.args.get('oss', default = '', type=str) + + if oss_url: + if os.path.isdir(path): + return make_response('dir is not support') + + global auth + oss_endpoint = os.getenv('OSS_ENDPOINT') + + bucket_and_object = oss_url[len('oss://'):] + + index = bucket_and_object.index('/') + bucket = bucket_and_object[:index] + object_key = bucket_and_object[index + 1:] + + logger.info('oss endpoint is ' + oss_endpoint) + logger.info('bucket is ' + bucket) + logger.info('object_key is ' + object_key) + + bucket = oss2.Bucket(auth, oss_endpoint, bucket) + + bucket.get_object_to_file(object_key, path) + + return make_response('copy from ' + oss_url + ' to fc:' + path) + else: + f = request.files['file'] + + if path.endswith('/') or os.path.isdir(path): + dst = os.path.join(path, f.filename) + else: + dst = path + + f.save(dst) + + html = f.filename + ' upload ' + dst + ' to success' + return make_response(html, 200) + +@app.route('/cat', methods=['GET']) +def cat(): + + f = request.args.get('file', default = '', type=str) + + path = os.path.join('/', f) + + if os.path.isfile(path): + return send_file(path) + else: + return make_response("file " + path + " not exist", 200) + +@app.route('/bash', methods=['POST']) +def bash(): + + cmd = request.args.get('cmd', default = '', type=str) + + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + p.wait() + + rs = ''.join(p.stdout.readlines()) + + return make_response(rs, 200) + +@app.route('/unzip', methods=['POST']) +def unzip(): + + f = request.args.get('file', default = '', type=str) + + if not os.path.isfile(f): + return make_response('file ' + f + ' is not exist', 400) + + if not f.endswith('.zip'): + return make_response('file ' + f + ' is not zip file', 400) + + extract_folder = f[:-4] + + if not os.path.exists(extract_folder): + os.mkdir(extract_folder) + + logger.info('zip file is ' + f) + + logger.info('extract_folder is ' + extract_folder) + + logger.info('begin unzip...') + + with zipfile.ZipFile(f, 'r') as z: + z.extractall(extract_folder) + logger.info('after unzip...') + + return make_response('extract file ' + f + ' to folder ' + extract_folder + ' success') + +@app.errorhandler(Exception) +def all_exception_handler(error): + return make_response(str(error), 500) + +def handler(environ, start_response): + + context = environ['fc.context'] + creds = context.credentials + + local = bool(os.getenv('local', "")) + + global auth + + if (local): + auth = oss2.Auth(creds.access_key_id, + creds.access_key_secret) + else: + auth = oss2.StsAuth(creds.access_key_id, + creds.access_key_secret, + creds.security_token) + + return app(environ, start_response) diff --git a/examples/nas_browser/template.yml b/examples/nas_browser/template.yml new file mode 100644 index 000000000..0e07ed9c9 --- /dev/null +++ b/examples/nas_browser/template.yml @@ -0,0 +1,37 @@ +ROSTemplateFormatVersion: '2015-09-01' +Transform: 'Aliyun::Serverless-2018-04-03' +Resources: + nasDemo: + Type: 'Aliyun::Serverless::Service' + Properties: + Description: 'fc nas test' + Policies: + - AliyunECSNetworkInterfaceManagementAccess + - AliyunOSSFullAccess + VpcConfig: + VpcId: 'vpc-bp12hm92gdpcjtai7ua82' + VSwitchIds: [ 'vsw-bp1gitru7oicyyb4uiylj' ] + SecurityGroupId: 'sg-bp1243pi65bw4cjj4bks' + NasConfig: + UserId: 10003 + GroupId: 10003 + MountPoints: + - ServerAddr: '012194b28f-ujc20.cn-hangzhou.nas.aliyuncs.com:/' + MountDir: '/mnt/nas' + + browser: + Type: 'Aliyun::Serverless::Function' + Properties: + Handler: index.handler + Runtime: python2.7 + CodeUri: './src' + Timeout: 100 + EnvironmentVariables: + ROOT_DIR: /mnt/nas + OSS_ENDPOINT: oss-cn-shanghai.aliyuncs.com + Events: + http-test: + Type: HTTP + Properties: + AuthType: ANONYMOUS + Methods: ['GET', 'POST', 'PUT'] \ No newline at end of file