diff options
author | Jan Lehnardt <jan@apache.org> | 2023-01-05 10:48:28 +0100 |
---|---|---|
committer | Jan Lehnardt <jan@apache.org> | 2023-01-05 12:46:52 +0100 |
commit | 3cc22f4a88d1b18f031c89172a2ede4d13135e89 (patch) | |
tree | 80ff9b996a2eb66b8b0058961f4edf8c8e254e8b | |
parent | 33ee2164e6e73429193bc353a2c7dfb9d27a7790 (diff) | |
download | couchdb-3cc22f4a88d1b18f031c89172a2ede4d13135e89.tar.gz |
feat: remove failed couch_plugins experiment
-rw-r--r-- | rebar.config.script | 1 | ||||
-rw-r--r-- | rel/reltool.config | 2 | ||||
-rw-r--r-- | src/couch/src/couch.app.src | 3 | ||||
-rw-r--r-- | src/couch_plugins/LICENSE | 202 | ||||
-rw-r--r-- | src/couch_plugins/Makefile.am | 40 | ||||
-rw-r--r-- | src/couch_plugins/README.md | 159 | ||||
-rw-r--r-- | src/couch_plugins/src/couch_plugins.app.src | 22 | ||||
-rw-r--r-- | src/couch_plugins/src/couch_plugins.erl | 322 | ||||
-rw-r--r-- | src/couch_plugins/src/couch_plugins_httpd.erl | 69 |
9 files changed, 1 insertions, 819 deletions
diff --git a/rebar.config.script b/rebar.config.script index c6aced66b..028aabe8f 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -120,7 +120,6 @@ SubDirs = [ "src/couch_index", "src/couch_mrview", "src/couch_replicator", - "src/couch_plugins", "src/couch_pse_tests", "src/couch_stats", "src/couch_peruser", diff --git a/rel/reltool.config b/rel/reltool.config index 2e3fdd8a0..fc691fae9 100644 --- a/rel/reltool.config +++ b/rel/reltool.config @@ -35,7 +35,6 @@ couch_index, couch_log, couch_mrview, - couch_plugins, couch_replicator, couch_stats, couch_event, @@ -99,7 +98,6 @@ {app, couch_index, [{incl_cond, include}]}, {app, couch_log, [{incl_cond, include}]}, {app, couch_mrview, [{incl_cond, include}]}, - {app, couch_plugins, [{incl_cond, include}]}, {app, couch_replicator, [{incl_cond, include}]}, {app, couch_stats, [{incl_cond, include}]}, {app, couch_event, [{incl_cond, include}]}, diff --git a/src/couch/src/couch.app.src b/src/couch/src/couch.app.src index 951cb1e37..8b0ddfb8e 100644 --- a/src/couch/src/couch.app.src +++ b/src/couch/src/couch.app.src @@ -61,8 +61,7 @@ {"_replicate", "{couch_replicator_httpd, handle_req}"}, {"_uuids", "{couch_httpd_misc_handlers, handle_uuids_req}"}, {"_stats", "{couch_stats_httpd, handle_stats_req}"}, - {"_session", "{couch_httpd_auth, handle_session_req}"}, - {"_plugins", "{couch_plugins_httpd, handle_req}"} + {"_session", "{couch_httpd_auth, handle_session_req}"} ]}, {httpd_db_handlers, [ {"_all_docs", "{couch_mrview_http, handle_all_docs_req}"}, diff --git a/src/couch_plugins/LICENSE b/src/couch_plugins/LICENSE deleted file mode 100644 index f6cd2bc80..000000000 --- a/src/couch_plugins/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - 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. diff --git a/src/couch_plugins/Makefile.am b/src/couch_plugins/Makefile.am deleted file mode 100644 index 37cd9d5c1..000000000 --- a/src/couch_plugins/Makefile.am +++ /dev/null @@ -1,40 +0,0 @@ -## 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. - -couch_pluginslibdir = $(localerlanglibdir)/couch_plugins-0.1 -couch_pluginsebindir = $(couch_pluginslibdir)/ebin - -couch_pluginsebin_DATA = $(compiled_files) - - -source_files = \ - src/couch_plugins.app.src \ - src/couch_plugins.erl \ - src/couch_plugins_httpd.erl - -compiled_files = \ - ebin/couch_plugins.app \ - ebin/couch_plugins.beam \ - ebin/couch_plugins_httpd.beam - -EXTRA_DIST = $(source_files) README.md -CLEANFILES = $(compiled_files) - -ebin/%.app: src/%.app.src - @mkdir -p ebin/ - sed -e "s|%version%|@version@|g" \ - < $< > $@ - -ebin/%.beam: src/%.erl $(include_files) - @mkdir -p ebin/ - $(ERLC) -Wall -I$(top_srcdir)/src -I$(top_srcdir)/src/couchdb \ - -o ebin/ $(ERLC_FLAGS) ${TEST} $<; diff --git a/src/couch_plugins/README.md b/src/couch_plugins/README.md deleted file mode 100644 index b00a080c1..000000000 --- a/src/couch_plugins/README.md +++ /dev/null @@ -1,159 +0,0 @@ -Heya, - -I couldn’t help myself thinking about plugin stuff and ended up -whipping up a proof of concept. - -Here’s a <1 minute demo video: - - https://dl.dropboxusercontent.com/u/82149/couchdb-plugins-demo.mov - -Alternative encoding: - - https://dl.dropboxusercontent.com/u/82149/couchdb-plugins-demo.m4v) - - -In my head the whole plugin idea is a very wide area, but I was so -intrigued by the idea of getting something running with a click on a -button in Futon. So I looked for a minimally viable plugin system. - - -## Design principles - -It took me a day to put this all together and this was only possible -because I took a lot of shortcuts. I believe they are all viable for a -first iteration of a plugins system: - -1. Install with one click on a button in Futon (or HTTP call) -2. Only pure Erlang plugins are allowed. -3. The plugin author must provide a binary package for each Erlang (and, - later, each CouchDB version). -4. Complete trust-based system. You trust me to not do any nasty things - when you click on the install button. No crypto, no nothing. Only - people who can commit to Futon can release new versions of plugins. -5. Minimal user-friendlyness: won’t install plugins that don’t match - the current Erlang version, gives semi-sensible error messages - (wrapped in a HTTP 500 response :) -6. Require a pretty strict format for binary releases. - - -## Roadmap - -Here’s a list of things this first iterations does and doesn’t do: - -- Pure Erlang plugins only. No C-dependencies, no JavaScript, no nothing. -- No C-dependencies. -- Install a plugin via Futon (or HTTP call). Admin only. -- A hardcoded list of plugins in Futon. -- Loads a pre-packaged, pre-compiled .tar.gz file from a URL. -- Only installs if Erlang version matches. -- No security checking of binaries. -- No identity checking of binaries. -- Register installed plugins in the config system. -- Make sure plugins start with the next restart of CouchDB. -- Uninstall a plugin via Futon (or HTTP call). Admin only. -- Show when a particular plugin is installed. -- Only installs if CouchDB version matches. -- Serve static web assets (for Futon/Fauxton) from `/_plugins/<name>/`. - -I hope you agree we can ship this with a few warnings so people can get a -hang of it. - - -A roadmap, progress and issues can be found here: - -https://issues.apache.org/jira/issues/?jql=component+%3D+Plugins+AND+project+%3D+COUCHDB+AND+resolution+%3D+Unresolved+ORDER+BY+priority+DESC - - - -## How it works - -This plugin system lives in `src/couch_plugins` and is a tiny CouchDB -module. - -It exposes one new API endpoint `/_plugins` that an admin user can -POST to. - -The additional Futon page lives at `/_utils/plugins.html` it is -hardcoded. - -Futon (or you) post an object to `/_plugins` with four properties: - - { - "name": "geocouch", // name of the plugin, must be unique - "url": "http://people.apache.org/~jan", // “base URL” for plugin releases (see below) - "version": "couchdb1.2.x_v0.3.0-11-g4ea0bea", // whatever version internal to the plugin - "checksums": { - "R15B03": "ZetgdHj2bY2w37buulWVf3USOZs=" // base64’d sha hash over the binary - } - } - -`couch_plugins` then attempts to download a .tar.gz from this -location: - - http://people.apache.org/~jan/geocouch-couchdb1.2.x_v0.3.0-12-g4ea0bea-R15B03.tar.gz - -It should be obvious how the URL is constructed from the POST data. -(This url is live, feel free to play around with this tarball). - -Next it calculates the sha hash for the downloaded .tar.gz file and -matches it against the correct version in the `checksums` parameter. - -If that succeeds, we unpack the .tar.gz file (currently in `/tmp`, -need to find a better place for this) and adds the extracted directory -to the Erlang code path -(`code:add_path("/tmp/couchdb_plugins/geocouch-couchdb1.2.x_v0.3.0-12-g4ea0bea-R15B03/ebin")`) -and loads the included application (`application:load(geocouch)`). - -Then it looks into the `./priv/default.d` directory that lives next to -`ebin/` in the plugin directory for configuration `.ini` files and loads them. -On next startup these configuration files are loaded after global defaults, -and before any local configuration. - -If that all goes to plan, we report success back to the HTTP caller. - -That’s it! :) - -It’s deceptively simple, probably does a few things very wrong and -leaves a few things open (see above). - -One open question I’d like an answer for is finding a good location to -unpack & install the plugin files that isn’t `tmp`. If the answer is -different for a pre-BigCouch/rcouch-merge and post-BigCouch/rcouch- -merge world, I’d love to know :) - - -## Code - -The main branch for this is 1867-feature-plugins: - - ASF: https://git-wip-us.apache.org/repos/asf?p=couchdb.git;a=log;h=refs/heads/1867-feature-plugins - GitHub: https://github.com/janl/couchdb/compare/apache:master...1867-feature-plugins - -I created a branch on GeoCouch that adds a few lines to its `Makefile` -that shows how a binary package is built: - - https://github.com/janl/geocouch/compare/couchbase:couchdb1.3.x...couchdb1.3.x-plugins - - -## Build - -Build CouchDB as usual: - - ./bootstrap - ./configure - make - make dev - ./utils/run - -* * * - -I hope you like this :) Please comment and improve heavily! - -Let me know if you have any questions :) - -If you have any criticism, please phrase it in a way that we can use -to improve this, thanks! - -Best, -Jan --- diff --git a/src/couch_plugins/src/couch_plugins.app.src b/src/couch_plugins/src/couch_plugins.app.src deleted file mode 100644 index 07d6b14d6..000000000 --- a/src/couch_plugins/src/couch_plugins.app.src +++ /dev/null @@ -1,22 +0,0 @@ -% 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. -{application, couch_plugins, - [ - {description, "A CouchDB Plugin Installer"}, - {vsn, git}, - {registered, []}, - {applications, [ - kernel, - stdlib - ]}, - {env, []} - ]}. diff --git a/src/couch_plugins/src/couch_plugins.erl b/src/couch_plugins/src/couch_plugins.erl deleted file mode 100644 index 97834134b..000000000 --- a/src/couch_plugins/src/couch_plugins.erl +++ /dev/null @@ -1,322 +0,0 @@ -% 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. --module(couch_plugins). --include_lib("couch/include/couch_db.hrl"). --export([install/1, uninstall/1]). - -% couch_plugins:install({"geocouch", "http://127.0.0.1:8000", "1.0.0", [{"R15B03", "+XOJP6GSzmuO2qKdnjO+mWckXVs="}]}). -% couch_plugins:install({"geocouch", "http://people.apache.org/~jan/", "couchdb1.2.x_v0.3.0-11-gd83ba22", [{"R15B03", "ZetgdHj2bY2w37buulWVf3USOZs="}]}). - -plugin_dir() -> - couch_config:get("couchdb", "plugin_dir"). - -log(T) -> - couch_log:debug("[couch_plugins] ~p ~n", [T]). - -%% "geocouch", "http://localhost:8000/dist", "1.0.0" --type plugin() :: {string(), string(), string(), list()}. --spec install(plugin()) -> ok | {error, string()}. -install({Name, _BaseUrl, Version, Checksums} = Plugin) -> - log("Installing " ++ Name), - - {ok, LocalFilename} = download(Plugin), - log("downloaded to " ++ LocalFilename), - - ok = verify_checksum(LocalFilename, Checksums), - log("checksum verified"), - - ok = untargz(LocalFilename), - log("extraction done"), - - ok = add_code_path(Name, Version), - log("added code path"), - - ok = register_plugin(Name, Version), - log("registered plugin"), - - load_config(Name, Version), - log("loaded config"), - - ok. - -% Idempotent uninstall, if you uninstall a non-existant -% plugin, you get an `ok`. --spec uninstall(plugin()) -> ok | {error, string()}. -uninstall({Name, _BaseUrl, Version, _Checksums}) -> - % unload config - ok = unload_config(Name, Version), - log("config unloaded"), - - % delete files - ok = delete_files(Name, Version), - log("files deleted"), - - % delete code path - ok = del_code_path(Name, Version), - log("deleted code path"), - - % unregister plugin - ok = unregister_plugin(Name), - log("unregistered plugin"), - - % done - ok. - -%% * * * - -%% Plugin Registration -%% On uninstall: -%% - add plugins/name = version to config -%% On uninstall: -%% - remove plugins/name from config - --spec register_plugin(string(), string()) -> ok. -register_plugin(Name, Version) -> - couch_config:set("plugins", Name, Version). - --spec unregister_plugin(string()) -> ok. -unregister_plugin(Name) -> - couch_config:delete("plugins", Name). - -%% * * * - -%% Load Config -%% Parses <plugindir>/priv/default.d/<pluginname.ini> and applies -%% the contents to the config system, or removes them on uninstall - --spec load_config(string(), string()) -> ok. -load_config(Name, Version) -> - loop_config(Name, Version, fun set_config/1). - --spec unload_config(string(), string()) -> ok. -unload_config(Name, Version) -> - loop_config(Name, Version, fun delete_config/1). - --spec loop_config(string(), string(), function()) -> ok. -loop_config(Name, Version, Fun) -> - lists:foreach( - fun(File) -> load_config_file(File, Fun) end, - filelib:wildcard(file_names(Name, Version)) - ). - --spec load_config_file(string(), function()) -> ok. -load_config_file(File, Fun) -> - {ok, Config} = couch_config:parse_ini_file(File), - lists:foreach(Fun, Config). - --spec set_config({{string(), string()}, string()}) -> ok. -set_config({{Section, Key}, Value}) -> - ok = couch_config:set(Section, Key, Value). - --spec delete_config({{string(), string()}, _Value}) -> ok. -delete_config({{Section, Key}, _Value}) -> - ok = couch_config:delete(Section, Key). - --spec file_names(string(), string()) -> string(). -file_names(Name, Version) -> - filename:join( - [ - plugin_dir(), - get_file_slug(Name, Version), - "priv", - "default.d", - "*.ini" - ] - ). - -%% * * * - -%% Code Path Management -%% The Erlang code path is where the Erlang runtime looks for `.beam` -%% files to load on, say, `application:load()`. Since plugin directories -%% are created on demand and named after CouchDB and Erlang versions, -%% we manage the Erlang code path semi-automatically here. - --spec add_code_path(string(), string()) -> ok | {error, bad_directory}. -add_code_path(Name, Version) -> - PluginPath = plugin_dir() ++ "/" ++ get_file_slug(Name, Version) ++ "/ebin", - case code:add_path(PluginPath) of - true -> - ok; - Else -> - couch_log:error("Failed to add PluginPath: '~s'", [PluginPath]), - Else - end. - --spec del_code_path(string(), string()) -> ok | {error, atom()}. -del_code_path(Name, Version) -> - PluginPath = plugin_dir() ++ "/" ++ get_file_slug(Name, Version) ++ "/ebin", - case code:del_path(PluginPath) of - true -> - ok; - _Else -> - couch_log:debug( - "Failed to delete PluginPath: '~s', ignoring", - [PluginPath] - ), - ok - end. - -%% * * * - --spec untargz(string()) -> {ok, string()} | {error, string()}. -untargz(Filename) -> - % read .gz file - {ok, GzData} = file:read_file(Filename), - % gunzip - log("unzipped"), - TarData = zlib:gunzip(GzData), - ok = filelib:ensure_dir(plugin_dir()), - % untar - erl_tar:extract({binary, TarData}, [{cwd, plugin_dir()}, keep_old_files]). - --spec delete_files(string(), string()) -> ok | {error, atom()}. -delete_files(Name, Version) -> - PluginPath = plugin_dir() ++ "/" ++ get_file_slug(Name, Version), - mochitemp:rmtempdir(PluginPath). - -% downloads a pluygin .tar.gz into a local plugins directory --spec download(string()) -> ok | {error, string()}. -download({Name, _BaseUrl, Version, _Checksums} = Plugin) -> - TargetFile = filename:join(mochitemp:gettempdir(), get_filename(Name, Version)), - case file_exists(TargetFile) of - %% wipe and redownload - true -> file:delete(TargetFile); - _Else -> ok - end, - Url = get_url(Plugin), - HTTPOptions = [ - % 30 seconds - {connect_timeout, 30 * 1000}, - % 30 seconds - {timeout, 30 * 1000} - ], - % todo: windows - Options = [ - % /tmp/something - {stream, TargetFile}, - {body_format, binary}, - {full_result, false} - ], - % todo: reduce to just httpc:request() - case httpc:request(get, {Url, []}, HTTPOptions, Options) of - {ok, _Result} -> - log("downloading " ++ Url), - {ok, TargetFile}; - Error -> - Error - end. - --spec verify_checksum(string(), list()) -> ok | {error, string()}. -verify_checksum(Filename, Checksums) -> - CouchDBVersion = couchdb_version(), - case proplists:get_value(CouchDBVersion, Checksums) of - undefined -> - couch_log:error( - "[couch_plugins] Can't find checksum for CouchDB Version" - " '~s'", - [CouchDBVersion] - ), - {error, no_couchdb_checksum}; - OTPChecksum -> - OTPRelease = erlang:system_info(otp_release), - case proplists:get_value(OTPRelease, OTPChecksum) of - undefined -> - couch_log:error( - "[couch_plugins] Can't find checksum for Erlang Version" - " '~s'", - [OTPRelease] - ), - {error, no_erlang_checksum}; - Checksum -> - do_verify_checksum(Filename, Checksum) - end - end. - --spec do_verify_checksum(string(), string()) -> ok | {error, string()}. -do_verify_checksum(Filename, Checksum) -> - couch_log:debug("Checking Filename: ~s", [Filename]), - case file:read_file(Filename) of - {ok, Data} -> - ComputedChecksum = binary_to_list(base64:encode(crypto:hash(sha, Data))), - case ComputedChecksum of - Checksum -> - ok; - _Else -> - couch_log:error( - "Checksum mismatch. Wanted: '~p'. Got '~p'", - [Checksum, ComputedChecksum] - ), - {error, checksum_mismatch} - end; - Error -> - Error - end. - -%% utils - --spec get_url(plugin()) -> string(). -get_url({Name, BaseUrl, Version, _Checksums}) -> - BaseUrl ++ "/" ++ get_filename(Name, Version). - --spec get_filename(string(), string()) -> string(). -get_filename(Name, Version) -> - get_file_slug(Name, Version) ++ ".tar.gz". - --spec get_file_slug(string(), string()) -> string(). -get_file_slug(Name, Version) -> - % OtpRelease does not include patch levels like the -1 in R15B03-1 - OTPRelease = erlang:system_info(otp_release), - CouchDBVersion = couchdb_version(), - string:join([Name, Version, OTPRelease, CouchDBVersion], "-"). - --spec file_exists(string()) -> boolean(). -file_exists(Filename) -> - does_file_exist(file:read_file_info(Filename)). --spec does_file_exist(term()) -> boolean(). -does_file_exist({error, enoent}) -> false; -does_file_exist(_Else) -> true. - -couchdb_version() -> - couch_server:get_version(short). - -% installing a plugin: -% - POST /_plugins -d {plugin-def} -% - get plugin definition -% - get download URL (matching erlang version) -% - download archive -% - match checksum -% - untar-gz archive into a plugins dir -% - code:add_path(“geocouch-{geocouch_version}-{erlang_version}/ebin”) -% - [cp geocouch-{geocouch_version}-{erlang_version}/etc/ ] -% - application:start(geocouch) -% - register plugin in plugin registry - -% Plugin registry impl: -% - _plugins database -% - pro: known db ops -% - con: no need for replication, needs to be system db etc. -% - _config/plugins namespace in config -% - pro: lightweight, fits rarely-changing nature better -% - con: potentially not flexible enough - - - -% /geocouch -% /geocouch/dist/ -% /geocouch/dist/geocouch-{geocouch_version}-{erlang_version}.tar.gz - -% tar.gz includes: -% geocouch-{geocouch_version}-{erlang_version}/ -% geocouch-{geocouch_version}-{erlang_version}/ebin -% [geocouch-{geocouch_version}-{erlang_version}/config/config.erlt] -% [geocouch-{geocouch_version}-{erlang_version}/share/] diff --git a/src/couch_plugins/src/couch_plugins_httpd.erl b/src/couch_plugins/src/couch_plugins_httpd.erl deleted file mode 100644 index 784f040fc..000000000 --- a/src/couch_plugins/src/couch_plugins_httpd.erl +++ /dev/null @@ -1,69 +0,0 @@ -% 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. --module(couch_plugins_httpd). - --export([handle_req/1]). - --include_lib("couch/include/couch_db.hrl"). - -handle_req(#httpd{method = 'POST'} = Req) -> - ok = couch_httpd:verify_is_server_admin(Req), - couch_httpd:validate_ctype(Req, "application/json"), - - {PluginSpec} = couch_httpd:json_body_obj(Req), - Url = binary_to_list(couch_util:get_value(<<"url">>, PluginSpec)), - Name = binary_to_list(couch_util:get_value(<<"name">>, PluginSpec)), - Version = binary_to_list(couch_util:get_value(<<"version">>, PluginSpec)), - Delete = couch_util:get_value(<<"delete">>, PluginSpec), - {Checksums0} = couch_util:get_value(<<"checksums">>, PluginSpec), - Checksums = parse_checksums(Checksums0), - - Plugin = {Name, Url, Version, Checksums}, - case do_install(Delete, Plugin) of - ok -> - couch_httpd:send_json(Req, 202, {[{ok, true}]}); - Error -> - couch_log:debug("Plugin Spec: ~p", [PluginSpec]), - couch_httpd:send_error(Req, {bad_request, Error}) - end; -% handles /_plugins/<pluginname>/<file> -% serves <plugin_dir>/<pluginname>-<pluginversion>-<otpversion>-<couchdbversion>/<file> -handle_req(#httpd{method = 'GET', path_parts = [_, Name0 | Path0]} = Req) -> - Name = ?b2l(Name0), - Path = lists:map(fun binary_to_list/1, Path0), - OTPRelease = erlang:system_info(otp_release), - PluginVersion = couch_config:get("plugins", Name), - CouchDBVersion = couch_server:get_version(short), - FullName = string:join([Name, PluginVersion, OTPRelease, CouchDBVersion], "-"), - FullPath = filename:join([FullName, "priv", "www", string:join(Path, "/")]) ++ "/", - couch_log:debug("Serving ~p from ~p", [FullPath, plugin_dir()]), - couch_httpd:serve_file(Req, FullPath, plugin_dir()); -handle_req(Req) -> - couch_httpd:send_method_not_allowed(Req, "POST"). - -plugin_dir() -> - couch_config:get("couchdb", "plugin_dir"). -do_install(false, Plugin) -> - couch_plugins:install(Plugin); -do_install(true, Plugin) -> - couch_plugins:uninstall(Plugin). - -parse_checksums(Checksums) -> - lists:map( - fun - ({K, {V}}) -> - {binary_to_list(K), parse_checksums(V)}; - ({K, V}) -> - {binary_to_list(K), binary_to_list(V)} - end, - Checksums - ). |