diff --git a/.github/apisix-config.yaml b/.github/apisix-config.yaml new file mode 100644 index 0000000000..2ff8ceba94 --- /dev/null +++ b/.github/apisix-config.yaml @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# If you want to set the specified configuration value, you can set the new +# in this file. For example if you want to specify the etcd address: +# + +etcd: + host: + - "http://etcd:2379" + +apisix: + allow_admin: # http://nginx.org/en/docs/http/ngx_http_access_module.html#allow + - 0.0.0.0/0 # If we don't set any IP list, then any IP access is allowed by default. diff --git a/.github/workflows/api_ci.yml b/.github/workflows/api_ci.yml index ec906daed6..039f26cfc1 100644 --- a/.github/workflows/api_ci.yml +++ b/.github/workflows/api_ci.yml @@ -23,13 +23,6 @@ jobs: env: ALLOW_NONE_AUTHENTICATION: yes - apisix: - image: johz/apisix:v1.4-github-action - env: - APISIX_ETCD_HOST: http://etcd:2379 - ports: - - 9080:9080 - mysql: image: mysql:8.0 env: @@ -43,14 +36,39 @@ jobs: - uses: actions/checkout@v2 + - name: run apisix + run: | + network=$(docker network ls | grep github_network | awk '{print $2}') + docker run --name apisix -d -p 9080:9080 \ + -v ${{ github.workspace }}/.github/apisix-config.yaml:/usr/local/apisix/conf/config.yaml \ + --network "$network" --network-alias apisix \ + apache/apisix:dev + sleep 5 + docker logs apisix + - name: setting up database run: | mysql -h 127.0.0.1 --port 3306 -u root -p123456 < ./api/script/db/schema.sql - - name: setup go - uses: actions/setup-go@v1 - with: - go-version: '1.13' + - name: ping apisix + run: | + curl 127.0.0.1:9080 + + - name: get lua lib + run: | + wget https://github.com/api7/dag-to-lua/archive/v1.0.tar.gz + sudo mkdir -p /go/api7-manager-api/dag-to-lua/ + tar -zxvf v1.0.tar.gz + sudo mv ./dag-to-lua-1.0/lib/* /go/api7-manager-api/dag-to-lua/ + + - name: install runtime + run: | + sudo apt-get update + sudo apt-get install lua5.1 + sudo add-apt-repository ppa:longsleep/golang-backports + sudo apt update + export GO111MOUDULE=on + sudo apt install golang-1.14-go - name: run test working-directory: ./api diff --git a/.github/workflows/api_cicd.yml b/.github/workflows/api_cicd.yml index a906135262..43cb2ae190 100644 --- a/.github/workflows/api_cicd.yml +++ b/.github/workflows/api_cicd.yml @@ -18,13 +18,6 @@ jobs: env: ALLOW_NONE_AUTHENTICATION: yes - apisix: - image: johz/apisix:v1.4-github-action - env: - APISIX_ETCD_HOST: http://etcd:2379 - ports: - - 9080:9080 - mysql: image: mysql:8.0 env: @@ -37,19 +30,39 @@ jobs: steps: - uses: actions/checkout@v2 + - name: run apisix + run: | + network=$(docker network ls | grep github_network | awk '{print $2}') + docker run --name apisix -d -p 9080:9080 \ + -v ${{ github.workspace }}/.github/apisix-config.yaml:/usr/local/apisix/conf/config.yaml \ + --network "$network" --network-alias apisix \ + apache/apisix:dev + sleep 5 + docker logs apisix + - name: setting up database run: | mysql -h 127.0.0.1 --port 3306 -u root -p123456 < ./api/script/db/schema.sql - - name: setup go - uses: actions/setup-go@v1 - with: - go-version: '1.13' + - name: ping apisix + run: | + curl 127.0.0.1:9080 + + - name: get lua lib + run: | + wget https://github.com/api7/dag-to-lua/archive/v1.0.tar.gz + sudo mkdir -p /go/api7-manager-api/dag-to-lua/ + tar -zxvf v1.0.tar.gz + sudo mv ./dag-to-lua-1.0/lib/* /go/api7-manager-api/dag-to-lua/ - - name: run test - working-directory: ./api + - name: install runtime run: | - go test ./... + sudo apt-get update + sudo apt-get install lua5.1 + sudo add-apt-repository ppa:longsleep/golang-backports + sudo apt update + export GO111MOUDULE=on + sudo apt install golang-1.14-go - uses: Azure/docker-login@v1 with: diff --git a/LICENSE b/LICENSE index a216e9c267..46a8d39f21 100644 --- a/LICENSE +++ b/LICENSE @@ -215,4 +215,13 @@ MIT licenses The following components are provided under the MIT License. See project link for details. The text of each license is also included at licenses/LICENSE-[project].txt. - files from ant-design-pro: https://github.com/ant-design/ant-design-pro MIT \ No newline at end of file + files from ant-design-pro: https://github.com/ant-design/ant-design-pro MIT + +======================================================================== +Apache 2.0 licenses +======================================================================== + +The following components are provided under the Apache 2.0 License. See project link for details. +The text of each license is also included at licenses/LICENSE-[project].txt. + + files from dag-to-lua: https://github.com/api7/dag-to-lua Apache 2.0 \ No newline at end of file diff --git a/api/Dockerfile b/api/Dockerfile index bba8f0aefb..c9e52badb3 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -19,20 +19,25 @@ FROM golang:1.13.8 AS build-env WORKDIR /go/src/github.com/apisix/manager-api COPY . . -RUN mkdir /root/manager-api \ +RUN mkdir /go/manager-api \ && go env -w GOPROXY=https://goproxy.io,direct \ && export GOPROXY=https://goproxy.io \ - && go build -o /root/manager-api/manager-api \ - && mv /go/src/github.com/apisix/manager-api/build.sh /root/manager-api/ \ - && mv /go/src/github.com/apisix/manager-api/conf/conf_preview.json /root/manager-api/conf.json \ + && go build -o /go/manager-api/manager-api \ + && mv /go/src/github.com/apisix/manager-api/build.sh /go/manager-api/ \ + && mv /go/src/github.com/apisix/manager-api/conf/conf_preview.json /go/manager-api/conf.json \ && rm -rf /go/src/github.com/apisix/manager-api \ && rm -rf /etc/localtime \ && ln -s /usr/share/zoneinfo/Hongkong /etc/localtime \ && dpkg-reconfigure -f noninteractive tzdata +RUN wget https://github.com/api7/dag-to-lua/archive/v1.0.tar.gz \ + && tar -zxvf v1.0.tar.gz \ + && mkdir /go/manager-api/dag-to-lua \ + && mv ./dag-to-lua-1.0/lib/* /go/manager-api/dag-to-lua/ + FROM alpine:3.11 -RUN mkdir /root/manager-api \ +RUN mkdir -p /go/manager-api \ && apk update \ && apk add ca-certificates \ && update-ca-certificates \ @@ -40,10 +45,11 @@ RUN mkdir /root/manager-api \ && echo "hosts: files dns" > /etc/nsswitch.conf \ && rm -rf /var/cache/apk/* +RUN apk add lua5.1 -WORKDIR /root/manager-api -COPY --from=build-env /root/manager-api/* /root/manager-api/ +WORKDIR /go/manager-api +COPY --from=build-env /go/manager-api/ /go/manager-api/ COPY --from=build-env /usr/share/zoneinfo/Hongkong /etc/localtime EXPOSE 8080 RUN chmod +x ./build.sh -CMD ["/bin/ash", "-c", "/root/manager-api/build.sh"] +CMD ["/bin/ash", "-c", "/go/manager-api/build.sh"] diff --git a/api/build.sh b/api/build.sh index 09e6e014ad..cd704900f6 100644 --- a/api/build.sh +++ b/api/build.sh @@ -26,6 +26,6 @@ sed -i -e "s%#syslogAddress#%`echo $SYSLOG_HOST`%g" ${pwd}/conf.json sed -i -e "s%#apisixBaseUrl#%`echo $APISIX_BASE_URL`%g" ${pwd}/conf.json sed -i -e "s%#apisixApiKey#%`echo $APISIX_API_KEY`%g" ${pwd}/conf.json -cd /root/manager-api +cd /go/manager-api exec ./manager-api diff --git a/api/conf/conf.go b/api/conf/conf.go index 1ed7ecb0d1..cbd7f8ebed 100644 --- a/api/conf/conf.go +++ b/api/conf/conf.go @@ -31,7 +31,7 @@ const PROD = "prod" const BETA = "beta" const DEV = "dev" const LOCAL = "local" -const confPath = "/root/manager-api/conf.json" +const confPath = "/go/manager-api/conf.json" const RequestId = "requestId" var ( @@ -81,10 +81,10 @@ type user struct { } type authenticationConfig struct { - Session struct { - Secret string - ExpireTime uint64 - } + Session struct { + Secret string + ExpireTime uint64 + } } var UserList = make(map[string]user, 1) @@ -129,12 +129,12 @@ func initAuthentication() { userList := configuration.Get("authentication.user").Array() // create user list - for _, item := range userList{ + for _, item := range userList { username := item.Map()["username"].String() password := item.Map()["password"].String() UserList[item.Map()["username"].String()] = user{Username: username, Password: password} } - AuthenticationConfig.Session.Secret = configuration.Get("authentication.session.secret").String() - AuthenticationConfig.Session.ExpireTime = configuration.Get("authentication.session.expireTime").Uint() + AuthenticationConfig.Session.Secret = configuration.Get("authentication.session.secret").String() + AuthenticationConfig.Session.ExpireTime = configuration.Get("authentication.session.expireTime").Uint() } } diff --git a/api/route/route.go b/api/route/route.go index db09f4f748..c86b437fbb 100644 --- a/api/route/route.go +++ b/api/route/route.go @@ -282,6 +282,12 @@ func findRoute(c *gin.Context) { return } result.Name = route.Name + var script map[string]interface{} + if err = json.Unmarshal([]byte(route.Script), &script); err != nil { + script = map[string]interface{}{} + } + result.Script = script + resp, _ := json.Marshal(result) c.Data(http.StatusOK, service.ContentType, resp) } diff --git a/api/script/db/schema.sql b/api/script/db/schema.sql index 95d4e60962..b1db5a0052 100644 --- a/api/script/db/schema.sql +++ b/api/script/db/schema.sql @@ -12,6 +12,7 @@ CREATE TABLE `routes` ( `priority` int NOT NULL DEFAULT 0, `state` int NOT NULL DEFAULT 1, -- 1-normal 0-disable `content` text, + `script` text, `content_admin_api` text, `create_time` bigint(20), `update_time` bigint(20), diff --git a/api/service/route.go b/api/service/route.go index 2ad9699f7d..23bf7864b4 100644 --- a/api/service/route.go +++ b/api/service/route.go @@ -19,6 +19,8 @@ package service import ( "encoding/json" "fmt" + "io/ioutil" + "os/exec" "time" "github.com/apisix/manager-api/conf" @@ -75,6 +77,11 @@ func (rd *Route) Parse(r *RouteRequest, arr *ApisixRouteRequest) error { } else { rd.Content = string(content) } + if script, err := json.Marshal(r.Script); err != nil { + return err + } else { + rd.Script = string(script) + } timestamp := time.Now().Unix() rd.CreateTime = timestamp rd.Priority = r.Priority @@ -172,6 +179,7 @@ type RouteRequest struct { UpstreamPath *UpstreamPath `json:"upstream_path,omitempty"` UpstreamHeader map[string]string `json:"upstream_header,omitempty"` Plugins map[string]interface{} `json:"plugins"` + Script map[string]interface{} `json:"script"` } func (r *ApisixRouteResponse) Parse() (*RouteRequest, error) { @@ -361,6 +369,7 @@ type ApisixRouteRequest struct { Upstream *Upstream `json:"upstream,omitempty"` UpstreamId string `json:"upstream_id,omitempty"` Plugins map[string]interface{} `json:"plugins,omitempty"` + Script string `json:"script,omitempty"` //Name string `json:"name"` } @@ -399,6 +408,7 @@ type Route struct { UpstreamId string `json:"upstream_id"` Priority int64 `json:"priority"` Content string `json:"content"` + Script string `json:"script"` ContentAdminApi string `json:"content_admin_api"` } @@ -513,9 +523,40 @@ func ToApisixRequest(routeRequest *RouteRequest) *ApisixRouteRequest { arr.Plugins = nil } + if routeRequest.Script != nil { + arr.Script, _ = generateLuaCode(routeRequest.Script) + } + return arr } +func generateLuaCode(script map[string]interface{}) (string, error) { + scriptString, err := json.Marshal(script) + if err != nil { + return "", err + } + + cmd := exec.Command("sh", "-c", + "cd /go/manager-api/dag-to-lua/ && lua cli.lua "+ + "'"+string(scriptString)+"'") + + logger.Info("generate conf:", string(scriptString)) + + stdout, _ := cmd.StdoutPipe() + defer stdout.Close() + if err := cmd.Start(); err != nil { + logger.Info("generate err:", err) + return "", err + } + + result, _ := ioutil.ReadAll(stdout) + resData := string(result) + + logger.Info("generated code:", resData) + + return resData, nil +} + func ToRoute(routeRequest *RouteRequest, arr *ApisixRouteRequest, u4 uuid.UUID, diff --git a/api/service/route_test.go b/api/service/route_test.go index b7fe5daf1a..98bf3e016b 100644 --- a/api/service/route_test.go +++ b/api/service/route_test.go @@ -20,6 +20,7 @@ import ( "encoding/json" "testing" + uuid "github.com/satori/go.uuid" "github.com/stretchr/testify/assert" ) @@ -203,3 +204,126 @@ func TestApisixRouteResponse_Parse(t *testing.T) { _, err := arr.Parse() a.Equal(nil, err) } + +// parse from params to RouteRequest +func TestRouteRequest_Parse(t *testing.T) { + a := assert.New(t) + param := []byte(`{ + "name": "API 名称", + "protocols": [ + "http", + "https" + ], + "hosts": [ + "www.baidu.com" + ], + "methods": [ + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "OPTIONS", + "PATCH" + ], + "redirect": { + "code": 302, + "uri": "11111" + }, + "vars": [], + "script": { + "rule": {}, + "conf": {} + } + }`) + routeRequest := &RouteRequest{} + err := routeRequest.Parse(param) + a.Nil(err) + a.Equal("API 名称", routeRequest.Name) + a.Equal(int64(0), routeRequest.Priority) + a.Equal(2, len(routeRequest.Script)) + a.Equal("/*", routeRequest.Uris[0]) +} + +// parse from RouteRequest and ApisixRouteRequest to Route +func TestRoute_Parse(t *testing.T) { + a := assert.New(t) + rrb := []byte(`{"name":"API 名称2","methods":["GET","HEAD","POST","PUT","DELETE","OPTIONS","PATCH"],"uris":["/*"],"hosts":["www.baidu.com"],"protocols":["http","https"],"redirect":{"code":302,"uri":"11111"},"plugins":null}`) + routeRequest := &RouteRequest{} + json.Unmarshal(rrb, routeRequest) + arrb := []byte(`{"priority":0,"methods":["GET","HEAD","POST","PUT","DELETE","OPTIONS","PATCH"],"uris":["/*"],"hosts":["www.baidu.com"],"plugins":{"redirect":{"code":302,"uri":"11111"}}, "script":{ + "rule":{ + "root": "11-22-33-44", + "11-22-33-44":[ + [ + "code == 503", + "yy-uu-ii-oo" + ], + [ + "", + "vv-cc-xx-zz" + ] + ] + }, + "conf":{ + "11-22-33-44":{ + "name": "limit-count", + "conf": { + "count":2, + "time_window":60, + "rejected_code":503, + "key":"remote_addr" + } + }, + "yy-uu-ii-oo":{ + "name": "response-rewrite", + "conf": { + "body":{"code":"ok","message":"request has been limited."}, + "headers":{ + "X-limit-status": "limited" + } + } + }, + "vv-cc-xx-zz":{ + "name": "response-rewrite", + "conf": { + "body":{"code":"ok","message":"normal request"}, + "headers":{ + "X-limit-status": "normal" + } + } + } + } +} }`) + arr := &ApisixRouteRequest{} + json.Unmarshal(arrb, arr) + + rd := &Route{} + err := rd.Parse(routeRequest, arr) + a.Nil(err) +} + +// parse Route +func TestToRoute(t *testing.T) { + a := assert.New(t) + b1 := []byte(`{"name":"API 名称2","methods":["GET","HEAD","POST","PUT","DELETE","OPTIONS","PATCH"],"uris":["/*"],"hosts":["www.baidu.com"],"protocols":["http","https"],"redirect":{"code":302,"uri":"11111"},"plugins":null}`) + rr := &RouteRequest{} + err := json.Unmarshal(b1, &rr) + a.Nil(err) + + b2 := []byte(`{"priority":0,"methods":["GET","HEAD","POST","PUT","DELETE","OPTIONS","PATCH"],"uris":["/*"],"hosts":["www.baidu.com"],"plugins":{"redirect":{"code":302,"uri":"11111"}},"script":"function(vars, opts) return vars[\"arg_key\"] == \"a\" or vars[\"arg_key\"] == \"b\" end"}`) + arr := &ApisixRouteRequest{} + err = json.Unmarshal(b2, &arr) + a.Nil(err) + + b3 := []byte(`{"action":"set","node":{"value":{"id":"","name":"","priority":0,"methods":["GET","HEAD","POST","PUT","DELETE","OPTIONS","PATCH"],"uris":["/*"],"hosts":["www.baidu.com"],"vars":null,"plugins":{"redirect":{"code":302,"ret_code":302,"uri":"11111"}}},"modifiedIndex":75}}`) + arp := &ApisixRouteResponse{} + err = json.Unmarshal(b3, &arp) + a.Nil(err) + + u4 := uuid.NewV4() + route, err := ToRoute(rr, arr, u4, arp) + a.Nil(err) + t.Log(route.Uris) + a.Equal("[\"/*\"]", route.Uris) +} diff --git a/compose/manager_conf/build.sh b/compose/manager_conf/build.sh index 03e0db3946..22f2e2f47a 100755 --- a/compose/manager_conf/build.sh +++ b/compose/manager_conf/build.sh @@ -33,5 +33,5 @@ sed -i -e "s%#syslogAddress#%`echo $SYSLOG_HOST`%g" ${pwd}/conf.json sed -i -e "s%#apisixBaseUrl#%`echo $APISIX_BASE_URL`%g" ${pwd}/conf.json sed -i -e "s%#apisixApiKey#%`echo $APISIX_API_KEY`%g" ${pwd}/conf.json -cd /root/manager-api +cd /go/manager-api exec ./manager-api diff --git a/licenses/LICENSE-dag-to-lua.txt b/licenses/LICENSE-dag-to-lua.txt new file mode 100644 index 0000000000..91a6a5701f --- /dev/null +++ b/licenses/LICENSE-dag-to-lua.txt @@ -0,0 +1,204 @@ +Copyright (c) 2019 ShenZhen ZhiLiu Technology Co., Ltd +Copyright (c) 2017 Julien Desgats + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file