diff -Nru erlang-cuttlefish-2.0.11+dfsg/appveyor.yml erlang-cuttlefish-3.0.1/appveyor.yml --- erlang-cuttlefish-2.0.11+dfsg/appveyor.yml 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/appveyor.yml 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1,12 @@ +build: off + +# Erlang 22.3 is only available in this image +image: "Visual Studio 2019" + +install: + - curl -OL https://s3.amazonaws.com/rebar3/rebar3 + +test_script: + - escript ./rebar3 do compile,eunit,dialyzer + +deploy: false diff -Nru erlang-cuttlefish-2.0.11+dfsg/ChangeLog.md erlang-cuttlefish-3.0.1/ChangeLog.md --- erlang-cuttlefish-2.0.11+dfsg/ChangeLog.md 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/ChangeLog.md 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1,44 @@ +# Cuttlefish Change Log + +## Next version (in development) + +No changes yet. + +## 3.0.1 (Mar 26, 2021) + +* Fix `$(< filename)` include directive parsing (#25). + +## 3.0.0 (Mar 13, 2021) + +### OTP Logger Instead of Lager + +The library now uses standard OTP logger instead of Lager. + +Log entries use a very similar format but the timestamp +formatting has changed. + +In older releases with Lager: + +``` +15:12:26.054 [info] No app.config or vm.args detected in /etc, activating cuttlefish +``` + +In 3.0, on a cutting edge Erlang version: + +``` +2021-03-08T15:14:55.963768+03:00 [info] No app.config or vm.args detected in /etc, activating cuttlefish +``` + +GitHub issue: [#19](https://github.com/Kyorai/cuttlefish/issues/19). + + +## 2.7.0 (Mar 7, 2021) + +### Erlang 24 Support + +Cuttlefish is now compatible (builds, runs) with Erlang 24. + +### Older Erlang Releases Support Dropped + +Cuttlefish now supports Erlang 22 through 24. `2.6.0` was the last +release to support older Erlang versions, e.g. 18 or 19. diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/changelog erlang-cuttlefish-3.0.1/debian/changelog --- erlang-cuttlefish-2.0.11+dfsg/debian/changelog 2018-12-10 00:07:52.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/changelog 2022-01-09 17:44:32.000000000 +0000 @@ -1,3 +1,21 @@ +erlang-cuttlefish (3.0.1-1) unstable; urgency=medium + + * Switch upstream source to fork at https://github.com/Kyorai/cuttlefish + * New upstream version 3.0.1 (Closes: #935279) + * Update (Build-)Depends + * Update Maintainers and Uploaders + * Update Standards-Version + * Add lintian overrides + * Switch debhelper compat level from 11 to 13 + * Change architecture from 'any' to 'all' + * Update debian/copyright + * Clean debian/rules + * Update debian/watch + * Add lintian overrides + * Add debian/upstream/metadata + + -- Philipp Huebner Sun, 09 Jan 2022 18:44:32 +0100 + erlang-cuttlefish (2.0.11+dfsg-4) unstable; urgency=medium * Fix FTBFS with Erlang 21.1 (Closes: #909978) diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/compat erlang-cuttlefish-3.0.1/debian/compat --- erlang-cuttlefish-2.0.11+dfsg/debian/compat 2018-12-10 00:07:52.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/compat 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -11 diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/control erlang-cuttlefish-3.0.1/debian/control --- erlang-cuttlefish-2.0.11+dfsg/debian/control 2018-12-10 00:07:52.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/control 2022-01-09 17:41:38.000000000 +0000 @@ -1,31 +1,25 @@ Source: erlang-cuttlefish -Maintainer: LeoFS maintainers team -Uploaders: Nobuhiro Iwamatsu +Maintainer: Debian Erlang Packagers +Uploaders: Nobuhiro Iwamatsu , Philipp Huebner Section: devel Priority: optional -Build-Depends: debhelper (>= 11), - erlang-dev (>= 1:16), - erlang-eunit, - erlang-mnesia, - erlang-dialyzer, +Rules-Requires-Root: no +Build-Depends: debhelper-compat (= 13), dh-rebar, erlang-getopt, - erlang-lager (>= 3.2.4), - erlang-neotoma (>= 1.7.4) -Standards-Version: 4.1.4 + erlang-neotoma +Standards-Version: 4.6.0 Vcs-Browser: https://salsa.debian.org/erlang-team/packages/erlang-cuttlefish Vcs-Git: https://salsa.debian.org/erlang-team/packages/erlang-cuttlefish.git -Homepage: https://github.com/basho/cuttlefish +Homepage: https://github.com/Kyorai/cuttlefish Package: erlang-cuttlefish -Architecture: any -Depends: ${shlibs:Depends}, +Architecture: all +Depends: ${erlang:Depends}, + ${shlibs:Depends}, ${misc:Depends}, - erlang-base-hipe | erlang-base | ${erlang-abi:Depends}, - ${erlang:Depends}, erlang-getopt, - erlang-lager (>= 3.2.4), - erlang-neotoma (>= 1.7.4) + erlang-neotoma Description: Erlang/OTP library for sysctl-like syntax Cuttlefish is a library for Erlang applications that wish to walk the fine line between Erlang `app.config`s and a sysctl-like syntax. diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/copyright erlang-cuttlefish-3.0.1/debian/copyright --- erlang-cuttlefish-2.0.11+dfsg/debian/copyright 2018-12-10 00:07:52.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/copyright 2022-01-09 17:44:32.000000000 +0000 @@ -1,27 +1,18 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: cuttlefish -Upstream-Contact: https://github.com/basho/cuttlefish -Source: https://github.com/basho/cuttlefish -Files-Excluded: rebar +Upstream-Contact: https://github.com/Kyorai/cuttlefish +Source: https://github.com/Kyorai/cuttlefish Files: * -Copyright: 2013 Basho Technologies, Inc. All Rights Reserved -License: Apache-2.0 - -Files: src/cuttlefish_effective.erl src/cuttlefish_duration_parse.peg -Copyright: 2014 Basho Technologies, Inc. All Rights Reserved -License: Apache-2.0 - -Files: src/lager_stderr_backend.erl -Copyright: 2011-2012 Basho Technologies, Inc. All Rights Reserved -License: Apache-2.0 - -Files: test/cuttlefish_test_group_leader.erl -Copyright: 2012 Basho Technologies, Inc +Copyright: 2012-2017 Basho Technologies, Inc. All Rights Reserved. + 2019 Pivotal Software, Inc. All rights reserved. + 2020 VMware, Inc. or its affiliates. All rights reserved. + 2021 Kyorai License: Apache-2.0 Files: debian/* -Copyright: Copyright 2012, Nobuhiro Iwamatsu +Copyright: 2012-2018 Nobuhiro Iwamatsu + 2022 Philipp Huebner License: Apache-2.0 License: Apache-2.0 diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/dh-rebar.conf erlang-cuttlefish-3.0.1/debian/dh-rebar.conf --- erlang-cuttlefish-2.0.11+dfsg/debian/dh-rebar.conf 2018-12-10 00:07:52.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/dh-rebar.conf 2022-01-09 17:17:38.000000000 +0000 @@ -1,6 +1 @@ EXEC_REBAR_COMMANDS=compile -PKG_NAME= -PKG_VARSION= -#REBAR_LIB_DIR= -#REBAR_BIN_DIR= -#REBAR_INCLUDE_DIR= diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/gbp.conf erlang-cuttlefish-3.0.1/debian/gbp.conf --- erlang-cuttlefish-2.0.11+dfsg/debian/gbp.conf 2018-12-10 00:07:52.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/gbp.conf 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -[DEFAULT] -upstream-branch = upstream -debian-branch = master -upstream-tag = upstream/%(version)s -compression = xz - -[buildpackage] -export-dir = ../build-area/ diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/lintian-overrides erlang-cuttlefish-3.0.1/debian/lintian-overrides --- erlang-cuttlefish-2.0.11+dfsg/debian/lintian-overrides 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/lintian-overrides 2022-01-09 17:31:05.000000000 +0000 @@ -0,0 +1 @@ +erlang-cuttlefish: repeated-path-segment lib usr/lib/erlang/lib/ diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/patches/fix_rebar_path erlang-cuttlefish-3.0.1/debian/patches/fix_rebar_path --- erlang-cuttlefish-2.0.11+dfsg/debian/patches/fix_rebar_path 2018-12-10 00:07:52.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/patches/fix_rebar_path 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -Origin: vendor -Forwarded: no -Reviewed-By: Nobuhiro Iwamatsu -Last-Update: <2016-02-21> - ---- erlang-cuttlefish-2.0.5+dfsg.orig/Makefile -+++ erlang-cuttlefish-2.0.5+dfsg/Makefile -@@ -1,23 +1,27 @@ - DIALYZER_APPS = kernel stdlib sasl erts ssl tools os_mon runtime_tools crypto inets \ - public_key mnesia syntax_tools compiler - COMBO_PLT = $(HOME)/.cuttlefish_combo_dialyzer_plt -+REBAR_BIN := $(shell which rebar) -+ifeq ($(REBAR_BIN),) -+REBAR_BIN = ./rebar -+endif - - .PHONY: deps - - all: deps compile -- ./rebar skip_deps=true escriptize -+ $(REBAR_BIN) skip_deps=true escriptize - - deps: -- ./rebar get-deps -+ $(REBAR_BIN) get-deps - - docsclean: - @rm -rf doc/*.png doc/*.html doc/*.css doc/edoc-info - - compile: deps -- ./rebar compile -+ $(REBAR_BIN) compile - - clean: -- @./rebar clean -+ @$(REBAR_BIN) clean - - distclean: clean - @rm -rf cuttlefish deps -diff --git a/rebar.config b/rebar.config -index add37dd..309b453 100644 ---- a/rebar.config -+++ b/rebar.config -@@ -19,5 +19,5 @@ - - {post_hooks, [ - {"-win32", compile, "rebar escriptize"}, -- {"^((?!-win32).)*$", compile, "./rebar escriptize"} -+ {"^((?!-win32).)*$", compile, "rebar escriptize"} - ]}. -diff --git a/tools.mk b/tools.mk -index 8e0e1b9..19bcd51 100644 ---- a/tools.mk -+++ b/tools.mk -@@ -1,11 +1,16 @@ -+REBAR_BIN := $(shell which rebar) -+ifeq ($(REBAR_BIN),) -+REBAR_BIN = ./rebar -+endif -+ - test: compile -- ./rebar eunit skip_deps=true -+ $(REBAR_BIN) eunit skip_deps=true - - docs: -- ./rebar doc skip_deps=true -+ $(REBAR_BIN) doc skip_deps=true - - xref: compile -- ./rebar xref skip_deps=true -+ $(REBAR_BIN) xref skip_deps=true - - PLT ?= $(HOME)/.riak_combo_dialyzer_plt - LOCAL_PLT = .local_dialyzer_plt diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/patches/fix_version_string erlang-cuttlefish-3.0.1/debian/patches/fix_version_string --- erlang-cuttlefish-2.0.11+dfsg/debian/patches/fix_version_string 2018-12-10 00:07:52.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/patches/fix_version_string 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -Origin: vendor -Forwarded: not-needed -Reviewed-By: Nobuhiro Iwamatsu -Last-Update: <2016-10-23> - -diff --git a/src/cuttlefish.app.src b/src/cuttlefish.app.src -index 7cede4a..9f6a117 100644 ---- a/src/cuttlefish.app.src -+++ b/src/cuttlefish.app.src -@@ -1,7 +1,7 @@ - {application, cuttlefish, - [ - {description, "cuttlefish configuration abstraction"}, -- {vsn, git}, -+ {vsn, "2.0.10"}, - {registered, []}, - {applications, [ - kernel, diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/patches/remove_dep erlang-cuttlefish-3.0.1/debian/patches/remove_dep --- erlang-cuttlefish-2.0.11+dfsg/debian/patches/remove_dep 2018-12-10 00:07:52.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/patches/remove_dep 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -Origin: vendor -Forwarded: not-needed -Reviewed-By: Nobuhiro Iwamatsu -Last-Update: <2017-07-01> - -diff --git a/rebar.config b/rebar.config -index 4173ca9..4ddafc8 100644 ---- a/rebar.config -+++ b/rebar.config -@@ -11,12 +11,6 @@ - {xref_checks, []}. - {xref_queries, [{"(XC - UC) || (XU - X - B - \"(rebar.*|mustache)\" : Mod)", []}]}. - --{deps, [ -- {getopt, ".*", {git, "https://github.com/basho/getopt.git", {tag, "v0.8.2"}}}, -- {lager, ".*", {git, "https://github.com/basho/lager.git", {tag, "3.2.4"}}}, -- {neotoma, ".*", {git, "https://github.com/basho/neotoma.git", {tag, "1.7.4"}}} -- ]}. -- - {post_hooks, [ - {"-win32", compile, "rebar escriptize"}, - {"^((?!-win32).)*$", compile, "./rebar escriptize"} diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/patches/remove-warnings_as_errors erlang-cuttlefish-3.0.1/debian/patches/remove-warnings_as_errors --- erlang-cuttlefish-2.0.11+dfsg/debian/patches/remove-warnings_as_errors 2018-12-10 00:07:52.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/patches/remove-warnings_as_errors 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -Description: Remove warnings_as_errors from erl_opts - Fix ftbfs with following error: -Compiling src/cuttlefish_unit.erl failed: -DEBUG: Worker compilation failed: {{error, - {error,[], - [["src/cuttlefish_unit.erl:4: export_all flag enabled - all functions will be exported\n"]]}}, - {source,"src/cuttlefish_unit.erl"}} -Author: Nobuhiro Iwamatsu -Last-Update: 2017-08-19 - -diff --git a/rebar.config b/rebar.config -index e7d9835..abaa3ce 100644 ---- a/rebar.config -+++ b/rebar.config -@@ -1,6 +1,6 @@ - {require_otp_vsn, "R16|17|18|19|20|21"}. - --{erl_opts, [warnings_as_errors, {parse_transform, lager_transform}, debug_info, warn_untyped_record]}. -+{erl_opts, [{parse_transform, lager_transform}, debug_info, warn_untyped_record]}. - - {eunit_opts, [verbose]}. - {cover_enabled, true}. diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/patches/series erlang-cuttlefish-3.0.1/debian/patches/series --- erlang-cuttlefish-2.0.11+dfsg/debian/patches/series 2018-12-10 00:07:52.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -remove_dep -fix_rebar_path -fix_version_string -support_otp_21 -remove-warnings_as_errors diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/patches/support_otp_21 erlang-cuttlefish-3.0.1/debian/patches/support_otp_21 --- erlang-cuttlefish-2.0.11+dfsg/debian/patches/support_otp_21 2018-12-10 00:07:52.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/patches/support_otp_21 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -diff --git a/rebar.config b/rebar.config -index ad61941..4ac69cd 100644 ---- a/rebar.config -+++ b/rebar.config -@@ -1,4 +1,4 @@ --{require_otp_vsn, "R16|17|18"}. -+{require_otp_vsn, "R16|17|18|19|20|21"}. - - {erl_opts, [warnings_as_errors, {parse_transform, lager_transform}, debug_info, warn_untyped_record]}. - diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/rules erlang-cuttlefish-3.0.1/debian/rules --- erlang-cuttlefish-2.0.11+dfsg/debian/rules 2018-12-10 00:07:52.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/rules 2022-01-09 17:41:48.000000000 +0000 @@ -1,21 +1,18 @@ #!/usr/bin/make -f +export DH_VERBOSE=1 +export DEB_BUILD_MAINT_OPTIONS = hardening=+all +include /usr/share/dpkg/default.mk +export ERL_COMPILER_OPTIONS=deterministic -DPKG_EXPORT_BUILDFLAGS = 1 -include /usr/share/dpkg/buildflags.mk %: dh $@ --buildsystem=rebar --with rebar -override_dh_auto_test: -ifeq "$(disable_test)" "0" - rebar eunit -vv skip_deps=true -endif + +override_dh_auto_configure: + sed -i "s/git\}/\"$(DEB_VERSION_UPSTREAM)\"\}/" src/cuttlefish.app.src + override_dh_auto_clean: dh_auto_clean - rm -rf ebin/*.beam - rm -rf ebin/*.app - rm -rf c_src/*.o - rm -rf .eunit - rm -rf src/cuttlefish_duration_parse.erl - rm -rf src/conf_parse.erl + rm -rf .eunit .rebar ebin diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/source/lintian-overrides erlang-cuttlefish-3.0.1/debian/source/lintian-overrides --- erlang-cuttlefish-2.0.11+dfsg/debian/source/lintian-overrides 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/source/lintian-overrides 2022-01-09 17:31:24.000000000 +0000 @@ -0,0 +1 @@ +erlang-cuttlefish source: debian-watch-does-not-check-gpg-signature diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/upstream/metadata erlang-cuttlefish-3.0.1/debian/upstream/metadata --- erlang-cuttlefish-2.0.11+dfsg/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/upstream/metadata 2022-01-09 17:39:43.000000000 +0000 @@ -0,0 +1,5 @@ +Name: cuttlefish +Bug-Database: https://github.com/Kyorai/cuttlefish/issues +Bug-Submit: https://github.com/Kyorai/cuttlefish/issues/new +Repository: https://github.com/Kyorai/cuttlefish.git +Repository-Browse: https://github.com/Kyorai/cuttlefish diff -Nru erlang-cuttlefish-2.0.11+dfsg/debian/watch erlang-cuttlefish-3.0.1/debian/watch --- erlang-cuttlefish-2.0.11+dfsg/debian/watch 2018-12-10 00:07:52.000000000 +0000 +++ erlang-cuttlefish-3.0.1/debian/watch 2022-01-09 16:17:05.000000000 +0000 @@ -1,3 +1,3 @@ -version=3 -opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/cuttlefish-$1.tar.gz/,dversionmangle=s/\+dfsg// \ - https://github.com/basho/cuttlefish/tags .*/v?(\d\S*)\.tar\.gz +version=4 +opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/cuttlefish-$1.tar.gz/ \ + https://github.com/Kyorai/cuttlefish/tags .*/v?(\d\S*)\.tar\.gz diff -Nru erlang-cuttlefish-2.0.11+dfsg/.gitattributes erlang-cuttlefish-3.0.1/.gitattributes --- erlang-cuttlefish-2.0.11+dfsg/.gitattributes 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/.gitattributes 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1,2 @@ +test/value* text eol=lf +"test/dir with spaces/value 3" text eol=lf diff -Nru erlang-cuttlefish-2.0.11+dfsg/.gitignore erlang-cuttlefish-3.0.1/.gitignore --- erlang-cuttlefish-2.0.11+dfsg/.gitignore 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -.DS_Store -generated.config -generated.conf -ebin/* -.eunit -src/conf_parse.erl -deps -cuttlefish -*.sublime-project -*.sublime-workspace -log -.local_dialyzer_plt -src/cuttlefish_duration_parse.erl -erln8.config -test_fixtures/escript_prune_test/generated.config/* \ No newline at end of file diff -Nru erlang-cuttlefish-2.0.11+dfsg/LICENSE erlang-cuttlefish-3.0.1/LICENSE --- erlang-cuttlefish-2.0.11+dfsg/LICENSE 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/LICENSE 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1,202 @@ + + 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 -Nru erlang-cuttlefish-2.0.11+dfsg/Makefile erlang-cuttlefish-3.0.1/Makefile --- erlang-cuttlefish-2.0.11+dfsg/Makefile 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -DIALYZER_APPS = kernel stdlib sasl erts ssl tools os_mon runtime_tools crypto inets \ - public_key mnesia syntax_tools compiler -COMBO_PLT = $(HOME)/.cuttlefish_combo_dialyzer_plt - -.PHONY: deps - -all: deps compile - ./rebar skip_deps=true escriptize - -deps: - ./rebar get-deps - -docsclean: - @rm -rf doc/*.png doc/*.html doc/*.css doc/edoc-info - -compile: deps - ./rebar compile - -clean: - @./rebar clean - -distclean: clean - @rm -rf cuttlefish deps - -include tools.mk diff -Nru erlang-cuttlefish-2.0.11+dfsg/README.md erlang-cuttlefish-3.0.1/README.md --- erlang-cuttlefish-2.0.11+dfsg/README.md 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/README.md 2021-03-26 15:47:17.000000000 +0000 @@ -1,10 +1,26 @@ # Cuttlefish +[![GitHub Actions Status](https://github.com/Kyorai/cuttlefish/workflows/CI/badge.svg)](https://github.com/Kyorai/cuttlefish/actions) +[![Travis CI Build Status](https://travis-ci.org/Kyorai/cuttlefish.svg?branch=master)](https://travis-ci.org/Kyorai/cuttlefish) +[![Coverage Status](https://coveralls.io/repos/github/Kyorai/cuttlefish/badge.svg?branch=master)](https://coveralls.io/github/Kyorai/cuttlefish) +[![Hex version](https://img.shields.io/hexpm/v/cuttlefish.svg "Hex version")](https://hex.pm/packages/cuttlefish) + Cuttlefish is a library for Erlang applications that wish to walk the fine line between Erlang `app.config`s and a sysctl-like syntax. The name is a pun on the pronunciation of 'sysctl' and jokes are better explained. +This repository retains full history of the original repository, [basho/cuttlefish/](https://github.com/basho/cuttlefish/), +but intentionally cut ties with that repo to avoid confusion as to +where is the most up-to-date, maintained version is. + +This is the repository used to produce [Hex.pm releases](https://hex.pm/packages/cuttlefish) of the project. + +## Supported Erlang/OTP Versions + + * Cuttlefish 2.7.0 and later versions support Erlang 22 through 24 + * Cuttlefish releases up to and including 2.6.0 support Erlang/OTP 17 through 23 + ## Riak Disclaimer While this readme and test suite is Riak-heavy, the fact is that this @@ -88,7 +104,14 @@ * [node_package](https://github.com/basho/cuttlefish/wiki/Cuttlefish-for-node_package-users) * [non node_package](https://github.com/basho/cuttlefish/wiki/Cuttlefish-for-non-node_package-users) - ## Current Status Cuttlefish is ready for production deployments. + +## Re-generating parser + +``` +rebar3 as dev neotoma +``` + +Please see the *NOTE* in `src/conf_parse.peg` as well. diff -Nru erlang-cuttlefish-2.0.11+dfsg/rebar.config erlang-cuttlefish-3.0.1/rebar.config --- erlang-cuttlefish-2.0.11+dfsg/rebar.config 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/rebar.config 2021-03-26 15:47:17.000000000 +0000 @@ -1,23 +1,24 @@ -{require_otp_vsn, "R16|17|18"}. +{minimum_otp_vsn, "21.3"}. -{erl_opts, [warnings_as_errors, {parse_transform, lager_transform}, debug_info, warn_untyped_record]}. +{project_plugins, [rebar3_hex]}. -{eunit_opts, [verbose]}. -{cover_enabled, true}. +{erl_opts, [warnings_as_errors, + debug_info, + warn_untyped_record]}. + +{deps, [getopt]}. -{escript_emu_args, "%%! -escript main cuttlefish_escript -smp disable +A 0\n"}. -{escript_incl_apps, [goldrush, getopt, lager]}. +{escript_emu_args, "%%! -escript main cuttlefish_escript +S 1 +A 0\n"}. +{escript_incl_apps, [getopt, cuttlefish]}. +{escript_main_app, cuttlefish}. -{xref_checks, []}. -{xref_queries, [{"(XC - UC) || (XU - X - B - \"(rebar.*|mustache)\" : Mod)", []}]}. +{provider_hooks, [{post, [{compile, {default, escriptize}}]}]}. + +{eunit_opts, [verbose]}. +{cover_enabled, true}. +{cover_print_enabled, true}. +{cover_export_enabled, true}. -{deps, [ - {getopt, ".*", {git, "https://github.com/basho/getopt.git", {tag, "v0.8.2"}}}, - {lager, ".*", {git, "https://github.com/basho/lager.git", {tag, "3.2.4"}}}, - {neotoma, ".*", {git, "https://github.com/basho/neotoma.git", {tag, "1.7.4"}}} - ]}. - -{post_hooks, [ - {"-win32", compile, "rebar escriptize"}, - {"^((?!-win32).)*$", compile, "./rebar escriptize"} - ]}. +{profiles, [{dev, [{deps, [neotoma]}, + {plugins, [rebar3_neotoma_plugin]}]}, + {test, [{deps, [bbmustache]}]}]}. diff -Nru erlang-cuttlefish-2.0.11+dfsg/rebar.config.script erlang-cuttlefish-3.0.1/rebar.config.script --- erlang-cuttlefish-2.0.11+dfsg/rebar.config.script 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/rebar.config.script 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1,15 @@ +%% vim:ft=erlang: + +case os:getenv("TRAVIS_JOB_ID") of + false -> CONFIG; + JobId -> + %% coveralls.io. + [{plugins, [{coveralls, + {git, "https://github.com/markusn/coveralls-erl", + {branch, "master"}}}]} + ,{coveralls_coverdata, "_build/test/cover/eunit.coverdata"} + ,{coveralls_service_name, "travis-ci"} + ,{coveralls_service_job_id, JobId} + |CONFIG + ] +end. diff -Nru erlang-cuttlefish-2.0.11+dfsg/rebar.lock erlang-cuttlefish-3.0.1/rebar.lock --- erlang-cuttlefish-2.0.11+dfsg/rebar.lock 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/rebar.lock 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1,8 @@ +{"1.2.0", +[{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.2">>},0}]}. +[ +{pkg_hash,[ + {<<"getopt">>, <<"33D9B44289FE7AD08627DDFE1D798E30B2DA0033B51DA1B3A2D64E72CD581D02">>}]}, +{pkg_hash_ext,[ + {<<"getopt">>, <<"A0029AEA4322FB82A61F6876A6D9C66DC9878B6CB61FAA13DF3187384FD4EA26">>}]} +]. diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/conf_parse.erl erlang-cuttlefish-3.0.1/src/conf_parse.erl --- erlang-cuttlefish-2.0.11+dfsg/src/conf_parse.erl 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/conf_parse.erl 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1,489 @@ +-module(conf_parse). +-export([parse/1,file/1]). +-define(p_anything,true). +-define(p_charclass,true). +-define(p_choose,true). +-define(p_label,true). +-define(p_not,true). +-define(p_one_or_more,true). +-define(p_optional,true). +-define(p_scan,true). +-define(p_seq,true). +-define(p_string,true). +-define(p_zero_or_more,true). + + + + +%% ------------------------------------------------------------------- +%% +%% conf_parse: for all your .conf parsing needs. +%% +%% Copyright (c) 2013 Basho Technologies, Inc. All Rights Reserved. +%% Copyright (c) 2019 Pivotal Software, Inc. All rights reserved. +%% Copyright (c) 2020 VMware, Inc. or its affiliates. All rights reserved. +%% +%% This file is provided 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. +%% +%% ------------------------------------------------------------------- + +%% This module implements the parser for a sysctl-style +%% configuration format. Example: +%% +%% ``` +%% riak.local.node = riak@127.0.0.1 +%% riak.local.http = 127.0.0.1:8098 +%% riak.local.pb = 127.0.0.1:8087 +%% riak.local.storage.backend = bitcask''' +%% +%% This would parse into the following flat proplist: +%% +%% ``` +%% [{<<"riak.local.node">>,<<"riak@127.0.0.1">>}, +%% {<<"riak.local.http">>,<<"127.0.0.1:8098">>}, +%% {<<"riak.local.pb">>,<<"127.0.0.1:8087">>}, +%% {<<"riak.local.storage.backend">>,<<"bitcask">>}]''' +%% +%% Other modules in this application interpret and validate the +%% result of a successful parse. +%% @end +-define(line, true). +-define(FMT(F,A), lists:flatten(io_lib:format(F,A))). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). +-endif. + +%% @doc Only let through lines that are not comments or whitespace. +is_setting(ws) -> false; +is_setting([ws]) -> false; +is_setting(comment) -> false; +is_setting(_) -> true. + +%% @doc Removes escaped dots from keys +unescape_dots([$\\,$.|Rest]) -> + [$.|unescape_dots(Rest)]; +unescape_dots([]) -> []; +unescape_dots([C|Rest]) -> + [C|unescape_dots(Rest)]. + +-ifdef(TEST). +file_test() -> + Conf = conf_parse:file("test/riak.conf"), + ?assertEqual([ + {["ring_size"],"32"}, + {["anti_entropy"],"debug"}, + {["log","error","file"],"/var/log/error.log"}, + {["log","console","file"],"/var/log/console.log"}, + {["log","syslog"],"on"}, + {["listener","http","internal"],"127.0.0.1:8098"}, + {["listener","http","external"],"10.0.0.1:80"} + ], Conf), + ok. + +included_file_test() -> + Conf = conf_parse:file("test/include_file.conf"), + ?assertEqual([ + {include,<<"riak.conf">>} + ], Conf), + ok. + +included_dir_test() -> + Conf = conf_parse:file("test/include_dir.conf"), + ?assertEqual([ + {include,<<"conf.d/*.conf">>} + ], Conf), + ok. + +escaped_dots_are_removed_test() -> + Conf = conf_parse:parse("#comment\nsetting\\.0 = thing0\n"), + ?assertEqual([ + {["setting.0"],"thing0"} + ], Conf), + ok. + +utf8_test() -> + Conf = conf_parse:parse("setting = thing" ++ [338] ++ "\n"), + ?assertEqual([{["setting"], + {error, {conf_to_latin1, 1}} + }], Conf), + ok. + +gh_1_two_tab_test() -> + Conf = conf_parse:parse("setting0 = thing0\n\t\t\nsetting1 = thing1\n"), + ?assertEqual([ + {["setting0"],"thing0"}, + {["setting1"],"thing1"} + ], Conf), + ok. + +gh_1_three_tab_test() -> + Conf = conf_parse:parse("setting0 = thing0\n\t\t\t\nsetting1 = thing1\n"), + ?assertEqual([ + {["setting0"],"thing0"}, + {["setting1"],"thing1"} + ], Conf), + ok. + +-endif. + +-spec file(file:name()) -> any(). +file(Filename) -> + AbsFilename = filename:absname(Filename), + case erl_prim_loader:get_file(AbsFilename) of + {ok, Bin, _} -> parse(Bin); + error -> {error, undefined} + end. + +-spec parse(binary() | list()) -> any(). +parse(List) when is_list(List) -> parse(unicode:characters_to_binary(List)); +parse(Input) when is_binary(Input) -> + _ = setup_memo(), + Result = case 'config'(Input,{{line,1},{column,1}}) of + {AST, <<>>, _Index} -> AST; + Any -> Any + end, + release_memo(), Result. + +-spec 'config'(input(), index()) -> parse_result(). +'config'(Input, Index) -> + p(Input, Index, 'config', fun(I,D) -> (p_zero_or_more(fun 'line'/2))(I,D) end, fun(Node, _Idx) -> + [ L || L <- Node, is_setting(L) ] + end). + +-spec 'line'(input(), index()) -> parse_result(). +'line'(Input, Index) -> + p(Input, Index, 'line', fun(I,D) -> (p_choose([p_seq([p_choose([fun 'setting'/2, fun 'include'/2, fun 'comment'/2, p_one_or_more(fun 'ws'/2)]), p_choose([fun 'crlf'/2, fun 'eof'/2])]), fun 'crlf'/2]))(I,D) end, fun(Node, _Idx) -> + case Node of + [ Line, _EOL ] -> Line; + Line -> Line + end + end). + +-spec 'setting'(input(), index()) -> parse_result(). +'setting'(Input, Index) -> + p(Input, Index, 'setting', fun(I,D) -> (p_seq([p_zero_or_more(fun 'ws'/2), fun 'key'/2, p_zero_or_more(fun 'ws'/2), p_string(<<"=">>), p_zero_or_more(fun 'ws'/2), fun 'value'/2, p_zero_or_more(fun 'ws'/2), p_optional(fun 'comment'/2)]))(I,D) end, fun(Node, _Idx) -> + [ _, Key, _, _Eq, _, Value, _, _ ] = Node, + {Key, Value} + end). + +-spec 'key'(input(), index()) -> parse_result(). +'key'(Input, Index) -> + p(Input, Index, 'key', fun(I,D) -> (p_seq([p_label('head', fun 'word'/2), p_label('tail', p_zero_or_more(p_seq([p_string(<<".">>), fun 'word'/2])))]))(I,D) end, fun(Node, _Idx) -> + [{head, H}, {tail, T}] = Node, + [unicode:characters_to_list(H)| [ unicode:characters_to_list(W) || [_, W] <- T]] + end). + +-spec 'value'(input(), index()) -> parse_result(). +'value'(Input, Index) -> + p(Input, Index, 'value', fun(I,D) -> (p_one_or_more(p_seq([p_not(p_choose([p_seq([p_zero_or_more(fun 'ws'/2), fun 'crlf'/2]), fun 'comment'/2])), p_anything()])))(I,D) end, fun(Node, Idx) -> + case unicode:characters_to_binary(Node, utf8, latin1) of + {_Status, _Begining, _Rest} -> + {error, {conf_to_latin1, line(Idx)}}; + Bin -> + binary_to_list(Bin) + end + end). + +-spec 'comment'(input(), index()) -> parse_result(). +'comment'(Input, Index) -> + p(Input, Index, 'comment', fun(I,D) -> (p_seq([p_zero_or_more(fun 'ws'/2), p_string(<<"#">>), p_zero_or_more(p_seq([p_not(fun 'crlf'/2), p_anything()]))]))(I,D) end, fun(_Node, _Idx) ->comment end). + +-spec 'include'(input(), index()) -> parse_result(). +'include'(Input, Index) -> + p(Input, Index, 'include', fun(I,D) -> (p_seq([p_zero_or_more(fun 'ws'/2), p_string(<<"include">>), p_zero_or_more(fun 'ws'/2), fun 'included_file_or_dir'/2, p_optional(fun 'comment'/2)]))(I,D) end, fun(Node, _Idx) -> + [_, _Include, _, Included, _] = Node, + {include, Included} + end). + +-spec 'included_file_or_dir'(input(), index()) -> parse_result(). +'included_file_or_dir'(Input, Index) -> + p(Input, Index, 'included_file_or_dir', fun(I,D) -> (p_one_or_more(p_charclass(<<"[A-Za-z0-9-\_\.\*\\/]">>)))(I,D) end, fun(Node, _Idx) -> + unicode:characters_to_binary(Node, utf8, latin1) + end). + +-spec 'word'(input(), index()) -> parse_result(). +'word'(Input, Index) -> + p(Input, Index, 'word', fun(I,D) -> (p_one_or_more(p_choose([p_string(<<"\\.">>), p_charclass(<<"[A-Za-z0-9_-]">>)])))(I,D) end, fun(Node, _Idx) -> + unescape_dots(unicode:characters_to_list(Node)) + end). + +-spec 'crlf'(input(), index()) -> parse_result(). +'crlf'(Input, Index) -> + p(Input, Index, 'crlf', fun(I,D) -> (p_seq([p_optional(p_string(<<"\r">>)), p_string(<<"\n">>)]))(I,D) end, fun(_Node, _Idx) ->ws end). + +-spec 'eof'(input(), index()) -> parse_result(). +'eof'(Input, Index) -> + p(Input, Index, 'eof', fun(I,D) -> (p_not(p_anything()))(I,D) end, fun(_Node, _Idx) ->ws end). + +-spec 'ws'(input(), index()) -> parse_result(). +'ws'(Input, Index) -> + p(Input, Index, 'ws', fun(I,D) -> (p_one_or_more(p_charclass(<<"[\s\t]">>)))(I,D) end, fun(_Node, _Idx) ->ws end). + + + +-file("peg_includes.hrl", 1). +-type index() :: {{line, pos_integer()}, {column, pos_integer()}}. +-type input() :: binary(). +-type parse_failure() :: {fail, term()}. +-type parse_success() :: {term(), input(), index()}. +-type parse_result() :: parse_failure() | parse_success(). +-type parse_fun() :: fun((input(), index()) -> parse_result()). +-type xform_fun() :: fun((input(), index()) -> term()). + +-spec p(input(), index(), atom(), parse_fun(), xform_fun()) -> parse_result(). +p(Inp, StartIndex, Name, ParseFun, TransformFun) -> + case get_memo(StartIndex, Name) of % See if the current reduction is memoized + {ok, Memo} -> %Memo; % If it is, return the stored result + Memo; + _ -> % If not, attempt to parse + Result = case ParseFun(Inp, StartIndex) of + {fail,_} = Failure -> % If it fails, memoize the failure + Failure; + {Match, InpRem, NewIndex} -> % If it passes, transform and memoize the result. + Transformed = TransformFun(Match, StartIndex), + {Transformed, InpRem, NewIndex} + end, + memoize(StartIndex, Name, Result), + Result + end. + +-spec setup_memo() -> ets:tid(). +setup_memo() -> + put({parse_memo_table, ?MODULE}, ets:new(?MODULE, [set])). + +-spec release_memo() -> true. +release_memo() -> + ets:delete(memo_table_name()). + +-spec memoize(index(), atom(), parse_result()) -> true. +memoize(Index, Name, Result) -> + Memo = case ets:lookup(memo_table_name(), Index) of + [] -> []; + [{Index, Plist}] -> Plist + end, + ets:insert(memo_table_name(), {Index, [{Name, Result}|Memo]}). + +-spec get_memo(index(), atom()) -> {ok, term()} | {error, not_found}. +get_memo(Index, Name) -> + case ets:lookup(memo_table_name(), Index) of + [] -> {error, not_found}; + [{Index, Plist}] -> + case proplists:lookup(Name, Plist) of + {Name, Result} -> {ok, Result}; + _ -> {error, not_found} + end + end. + +-spec memo_table_name() -> ets:tid(). +memo_table_name() -> + get({parse_memo_table, ?MODULE}). + +-ifdef(p_eof). +-spec p_eof() -> parse_fun(). +p_eof() -> + fun(<<>>, Index) -> {eof, [], Index}; + (_, Index) -> {fail, {expected, eof, Index}} end. +-endif. + +-ifdef(p_optional). +-spec p_optional(parse_fun()) -> parse_fun(). +p_optional(P) -> + fun(Input, Index) -> + case P(Input, Index) of + {fail,_} -> {[], Input, Index}; + {_, _, _} = Success -> Success + end + end. +-endif. + +-ifdef(p_not). +-spec p_not(parse_fun()) -> parse_fun(). +p_not(P) -> + fun(Input, Index)-> + case P(Input,Index) of + {fail,_} -> + {[], Input, Index}; + {Result, _, _} -> {fail, {expected, {no_match, Result},Index}} + end + end. +-endif. + +-ifdef(p_assert). +-spec p_assert(parse_fun()) -> parse_fun(). +p_assert(P) -> + fun(Input,Index) -> + case P(Input,Index) of + {fail,_} = Failure-> Failure; + _ -> {[], Input, Index} + end + end. +-endif. + +-ifdef(p_seq). +-spec p_seq([parse_fun()]) -> parse_fun(). +p_seq(P) -> + fun(Input, Index) -> + p_all(P, Input, Index, []) + end. + +-spec p_all([parse_fun()], input(), index(), [term()]) -> parse_result(). +p_all([], Inp, Index, Accum ) -> {lists:reverse( Accum ), Inp, Index}; +p_all([P|Parsers], Inp, Index, Accum) -> + case P(Inp, Index) of + {fail, _} = Failure -> Failure; + {Result, InpRem, NewIndex} -> p_all(Parsers, InpRem, NewIndex, [Result|Accum]) + end. +-endif. + +-ifdef(p_choose). +-spec p_choose([parse_fun()]) -> parse_fun(). +p_choose(Parsers) -> + fun(Input, Index) -> + p_attempt(Parsers, Input, Index, none) + end. + +-spec p_attempt([parse_fun()], input(), index(), none | parse_failure()) -> parse_result(). +p_attempt([], _Input, _Index, Failure) -> Failure; +p_attempt([P|Parsers], Input, Index, FirstFailure)-> + case P(Input, Index) of + {fail, _} = Failure -> + case FirstFailure of + none -> p_attempt(Parsers, Input, Index, Failure); + _ -> p_attempt(Parsers, Input, Index, FirstFailure) + end; + Result -> Result + end. +-endif. + +-ifdef(p_zero_or_more). +-spec p_zero_or_more(parse_fun()) -> parse_fun(). +p_zero_or_more(P) -> + fun(Input, Index) -> + p_scan(P, Input, Index, []) + end. +-endif. + +-ifdef(p_one_or_more). +-spec p_one_or_more(parse_fun()) -> parse_fun(). +p_one_or_more(P) -> + fun(Input, Index)-> + Result = p_scan(P, Input, Index, []), + case Result of + {[_|_], _, _} -> + Result; + _ -> + {fail, {expected, Failure, _}} = P(Input,Index), + {fail, {expected, {at_least_one, Failure}, Index}} + end + end. +-endif. + +-ifdef(p_label). +-spec p_label(atom(), parse_fun()) -> parse_fun(). +p_label(Tag, P) -> + fun(Input, Index) -> + case P(Input, Index) of + {fail,_} = Failure -> + Failure; + {Result, InpRem, NewIndex} -> + {{Tag, Result}, InpRem, NewIndex} + end + end. +-endif. + +-ifdef(p_scan). +-spec p_scan(parse_fun(), input(), index(), [term()]) -> {[term()], input(), index()}. +p_scan(_, <<>>, Index, Accum) -> {lists:reverse(Accum), <<>>, Index}; +p_scan(P, Inp, Index, Accum) -> + case P(Inp, Index) of + {fail,_} -> {lists:reverse(Accum), Inp, Index}; + {Result, InpRem, NewIndex} -> p_scan(P, InpRem, NewIndex, [Result | Accum]) + end. +-endif. + +-ifdef(p_string). +-spec p_string(binary()) -> parse_fun(). +p_string(S) -> + Length = erlang:byte_size(S), + fun(Input, Index) -> + try + <> = Input, + {S, Rest, p_advance_index(S, Index)} + catch + error:{badmatch,_} -> {fail, {expected, {string, S}, Index}} + end + end. +-endif. + +-ifdef(p_anything). +-spec p_anything() -> parse_fun(). +p_anything() -> + fun(<<>>, Index) -> {fail, {expected, any_character, Index}}; + (Input, Index) when is_binary(Input) -> + <> = Input, + {<>, Rest, p_advance_index(<>, Index)} + end. +-endif. + +-ifdef(p_charclass). +-spec p_charclass(string() | binary()) -> parse_fun(). +p_charclass(Class) -> + {ok, RE} = re:compile(Class, [unicode, dotall]), + fun(Inp, Index) -> + case re:run(Inp, RE, [anchored]) of + {match, [{0, Length}|_]} -> + {Head, Tail} = erlang:split_binary(Inp, Length), + {Head, Tail, p_advance_index(Head, Index)}; + _ -> {fail, {expected, {character_class, binary_to_list(Class)}, Index}} + end + end. +-endif. + +-ifdef(p_regexp). +-spec p_regexp(binary()) -> parse_fun(). +p_regexp(Regexp) -> + {ok, RE} = re:compile(Regexp, [unicode, dotall, anchored]), + fun(Inp, Index) -> + case re:run(Inp, RE) of + {match, [{0, Length}|_]} -> + {Head, Tail} = erlang:split_binary(Inp, Length), + {Head, Tail, p_advance_index(Head, Index)}; + _ -> {fail, {expected, {regexp, binary_to_list(Regexp)}, Index}} + end + end. +-endif. + +-ifdef(line). +-spec line(index() | term()) -> pos_integer() | undefined. +line({{line,L},_}) -> L; +line(_) -> undefined. +-endif. + +-ifdef(column). +-spec column(index() | term()) -> pos_integer() | undefined. +column({_,{column,C}}) -> C; +column(_) -> undefined. +-endif. + +-spec p_advance_index(input() | unicode:charlist() | pos_integer(), index()) -> index(). +p_advance_index(MatchedInput, Index) when is_list(MatchedInput) orelse is_binary(MatchedInput)-> % strings + lists:foldl(fun p_advance_index/2, Index, unicode:characters_to_list(MatchedInput)); +p_advance_index(MatchedInput, Index) when is_integer(MatchedInput) -> % single characters + {{line, Line}, {column, Col}} = Index, + case MatchedInput of + $\n -> {{line, Line+1}, {column, 1}}; + _ -> {{line, Line}, {column, Col+1}} + end. diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/conf_parse.peg erlang-cuttlefish-3.0.1/src/conf_parse.peg --- erlang-cuttlefish-2.0.11+dfsg/src/conf_parse.peg 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/conf_parse.peg 2021-03-26 15:47:17.000000000 +0000 @@ -3,6 +3,8 @@ %% conf_parse: for all your .conf parsing needs. %% %% Copyright (c) 2013 Basho Technologies, Inc. All Rights Reserved. +%% Copyright (c) 2019 Pivotal Software, Inc. All rights reserved. +%% Copyright (c) 2020 VMware, Inc. or its affiliates. All rights reserved. %% %% This file is provided to you under the Apache License, %% Version 2.0 (the "License"); you may not use this file @@ -20,14 +22,32 @@ %% %% ------------------------------------------------------------------- +% ------------------------------------------------------------------- +% NOTE: IMPORTANT +% +% After using neotoma to re-generate conf_parse.erl, you MUST +% edit that file to change the exported file/1 function to this code: +% +% -spec file(file:name()) -> any(). +% file(Filename) -> +% AbsFilename = filename:absname(Filename), +% case erl_prim_loader:get_file(AbsFilename) of +% {ok, Bin, _} -> parse(Bin); +% error -> {error, undefined} +% end. +% +% The reason is that the above code allows for reading cuttlefish +% schemas from .ez archives +% ------------------------------------------------------------------- + %% A configuration file may have zero-or-more lines. config <- line* %{ [ L || L <- Node, is_setting(L) ] %}; -%% Lines are actual settings, comments, or horizontal whitespace, +%% Lines are actual settings, includes, comments, or horizontal whitespace, %% terminated by an end-of-line or end-of-file. -line <- ((setting / comment / ws+) (crlf / eof)) / crlf %{ +line <- ((setting / include / comment / ws+) (crlf / eof)) / crlf %{ case Node of [ Line, _EOL ] -> Line; Line -> Line @@ -61,6 +81,16 @@ %% allowed. comment <- ws* "#" (!crlf .)* `comment`; +%% An include is a line that begins with 'include' and something. +include <- ws* "include" ws* included_file_or_dir comment? %{ + [_, _Include, _, Included, _] = Node, + {include, Included} +%}; + +included_file_or_dir <- [A-Za-z0-9-\_\.\*\/]+ %{ + unicode:characters_to_binary(Node, utf8, latin1) +%}; + %% A word is one or more of letters, numbers and dashes or %% underscores. word <- ("\\." / [A-Za-z0-9_-])+ %{ @@ -75,7 +105,7 @@ eof <- !. `ws`; %% Whitespace is either spaces or tabs. -ws <- [ \t] `ws`; +ws <- [ \t]+ `ws`; % Erlang code %{ @@ -85,6 +115,8 @@ %% conf_parse: for all your .conf parsing needs. %% %% Copyright (c) 2013 Basho Technologies, Inc. All Rights Reserved. +%% Copyright (c) 2019 Pivotal Software, Inc. All rights reserved. +%% Copyright (c) 2020 VMware, Inc. or its affiliates. All rights reserved. %% %% This file is provided to you under the Apache License, %% Version 2.0 (the "License"); you may not use this file @@ -127,11 +159,11 @@ -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. %% @doc Only let through lines that are not comments or whitespace. is_setting(ws) -> false; +is_setting([ws]) -> false; is_setting(comment) -> false; is_setting(_) -> true. @@ -144,7 +176,7 @@ -ifdef(TEST). file_test() -> - Conf = conf_parse:file("../test/riak.conf"), + Conf = conf_parse:file("test/riak.conf"), ?assertEqual([ {["ring_size"],"32"}, {["anti_entropy"],"debug"}, @@ -156,11 +188,49 @@ ], Conf), ok. +included_file_test() -> + Conf = conf_parse:file("test/include_file.conf"), + ?assertEqual([ + {include,<<"riak.conf">>} + ], Conf), + ok. + +included_dir_test() -> + Conf = conf_parse:file("test/include_dir.conf"), + ?assertEqual([ + {include,<<"conf.d/*.conf">>} + ], Conf), + ok. + +escaped_dots_are_removed_test() -> + Conf = conf_parse:parse("#comment\nsetting\\.0 = thing0\n"), + ?assertEqual([ + {["setting.0"],"thing0"} + ], Conf), + ok. + utf8_test() -> Conf = conf_parse:parse("setting = thing" ++ [338] ++ "\n"), ?assertEqual([{["setting"], {error, {conf_to_latin1, 1}} }], Conf), ok. + +gh_1_two_tab_test() -> + Conf = conf_parse:parse("setting0 = thing0\n\t\t\nsetting1 = thing1\n"), + ?assertEqual([ + {["setting0"],"thing0"}, + {["setting1"],"thing1"} + ], Conf), + ok. + +gh_1_three_tab_test() -> + Conf = conf_parse:parse("setting0 = thing0\n\t\t\t\nsetting1 = thing1\n"), + ?assertEqual([ + {["setting0"],"thing0"}, + {["setting1"],"thing1"} + ], Conf), + ok. + -endif. %} diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_advanced.erl erlang-cuttlefish-3.0.1/src/cuttlefish_advanced.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_advanced.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_advanced.erl 2021-03-26 15:47:17.000000000 +0000 @@ -26,7 +26,6 @@ -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. %% @doc this function overlays the values in proplist 'AdvancedConfig' diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish.app.src erlang-cuttlefish-3.0.1/src/cuttlefish.app.src --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish.app.src 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish.app.src 2021-03-26 15:47:17.000000000 +0000 @@ -1,11 +1,8 @@ -{application, cuttlefish, - [ - {description, "cuttlefish configuration abstraction"}, - {vsn, git}, - {registered, []}, - {applications, [ - kernel, - stdlib - ]}, - {env, []} - ]}. +{application,cuttlefish, + [{description,"cuttlefish configuration abstraction"}, + {vsn,"3.0.1"}, + {registered,[]}, + {applications,[kernel,stdlib]}, + {env,[]}, + {licenses,["Apache"]}, + {links,[{"GitHub","https://github.com/Kyorai/cuttlefish"}]}]}. diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_bytesize.erl erlang-cuttlefish-3.0.1/src/cuttlefish_bytesize.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_bytesize.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_bytesize.erl 2021-03-26 15:47:17.000000000 +0000 @@ -28,7 +28,6 @@ -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. -export([parse/1, to_string/1]). diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_conf.erl erlang-cuttlefish-3.0.1/src/cuttlefish_conf.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_conf.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_conf.erl 2021-03-26 15:47:17.000000000 +0000 @@ -21,6 +21,8 @@ %% ------------------------------------------------------------------- -module(cuttlefish_conf). +-include_lib("kernel/include/logger.hrl"). + -export([ generate/1, generate_file/2, @@ -69,18 +71,61 @@ {errorlist, [{error, {file_open, {Filename, Reason}}}]}; {_Conf, Remainder, {{line, L}, {column, C}}} when is_binary(Remainder) -> {errorlist, [{error, {conf_syntax, {Filename, {L, C}}}}]}; - Conf -> + Conf0 -> + % go through the conf looking for include directives, fold all into + % a single proplist + Conf = fold_conf_files(Filename, Conf0), %% Conf is a proplist, check if any of the values are cuttlefish_errors {_, Values} = lists:unzip(Conf), case cuttlefish_error:filter(Values) of {errorlist, []} -> - remove_duplicates(Conf); + % expand any non-literal values (ie. included values) + expand_values(Filename, remove_duplicates(Conf)); {errorlist, ErrorList} -> NewErrorList = [ {error, {in_file, {Filename, E}}} || {error, E} <- ErrorList ], {errorlist, NewErrorList} end end. +fold_conf_files(Filename, Conf0) -> + lists:foldl(fun({include, Included}, Acc) -> + DirName = binary_to_list(filename:join(filename:dirname(Filename), + filename:dirname(Included))), + BaseName = binary_to_list(filename:basename(Included)), + Acc ++ lists:flatten( + [file(filename:join(DirName, F)) || F <- + filelib:wildcard(BaseName, DirName)]); + (KeyValue, Acc) -> + Acc ++ [KeyValue] + end, + [], + Conf0). + +expand_values(Filename, Conf) -> + lists:map(fun({K, Value0}) -> + case re:run(Value0, "^\\$\\(\\<(.+)\\)$", [{capture, all_but_first, list}]) of + {match, [IncludeFilename0]} -> + % This is a value of the format "$( + {K, re:replace(Value, "[\n\r]$", "", [{return, list}])}; + {error, Reason} -> + throw({unable_to_open, IncludeFilename, Reason}) + end; + _ -> + % normal value, nothing to do + {K, Value0} + end + end, + Conf). + -spec generate([cuttlefish_mapping:mapping()]) -> [string()]. generate(Mappings) -> lists:foldl( @@ -96,6 +141,12 @@ _ = [ begin io:format(S, "~s~n", [lists:flatten(Line)]) end || Line <- ConfFileLines], + % add an include directive at the end that will allow + % other conf files in `conf.d` and have them get picked up + % in order to override settings + % example use case is a k8s configMap that is mapped as a file + % to conf.d/erlang_vm.conf + io:format(S, "include conf.d/*.conf~n~n", []), _ = file:close(S), ok. @@ -119,7 +170,7 @@ case Level of basic -> ok; Level -> - lager:warning("{level, ~p} has been deprecated. Use 'hidden' or '{hidden, true}'", [Level]) + _ = ?LOG_WARNING("{level, ~p} has been deprecated. Use 'hidden' or '{hidden, true}'", [Level]) end, case generate_element(Hidden, Level, Default, Commented) of @@ -206,7 +257,6 @@ -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. -ifdef(TEST). @@ -298,48 +348,90 @@ ?assertEqual(["## Hi!", "## Bye!", "## ", "## Acceptable values:", "## - text"], Comments). duplicates_test() -> - Conf = file("../test/multi1.conf"), + Conf = file("test/multi1.conf"), ?assertEqual(2, length(Conf)), ?assertEqual("3", proplists:get_value(["a","b","c"], Conf)), ?assertEqual("1", proplists:get_value(["a","b","d"], Conf)), ok. duplicates_multi_test() -> - Conf = files(["../test/multi1.conf", "../test/multi2.conf"]), + Conf = files(["test/multi1.conf", "test/multi2.conf"]), ?assertEqual(2, length(Conf)), ?assertEqual("4", proplists:get_value(["a","b","c"], Conf)), ?assertEqual("1", proplists:get_value(["a","b","d"], Conf)), ok. files_one_nonent_test() -> - Conf = files(["../test/multi1.conf", "../test/nonent.conf"]), - ?assertEqual({errorlist,[{error, {file_open, {"../test/nonent.conf", enoent}}}]}, Conf), + Conf = files(["test/multi1.conf", "test/nonent.conf"]), + ?assertMatch({errorlist,[{error, {file_open, {"test/nonent.conf", _}}}]}, Conf), ok. files_incomplete_parse_test() -> - Conf = file("../test/incomplete.conf"), - ?assertEqual({errorlist, [{error, {conf_syntax, {"../test/incomplete.conf", {3, 1}}}}]}, Conf), + Conf = file("test/incomplete.conf"), + ?assertEqual({errorlist, [{error, {conf_syntax, {"test/incomplete.conf", {3, 1}}}}]}, Conf), ok. generate_element_level_advanced_test() -> - cuttlefish_lager_test_backend:bounce(warning), + _ = cuttlefish_test_logging:set_up(), + _ = cuttlefish_test_logging:bounce(warning), assert_no_output({level, advanced}), - [Log] = cuttlefish_lager_test_backend:get_logs(), + [Log] = cuttlefish_test_logging:get_logs(), ?assertMatch({match, _}, re:run(Log, "{level, advanced} has been deprecated. Use 'hidden' or '{hidden, true}'")), ok. generate_element_level_intermediate_test() -> - cuttlefish_lager_test_backend:bounce(warning), + _ = cuttlefish_test_logging:set_up(), + _ = cuttlefish_test_logging:bounce(warning), assert_no_output({level, intermediate}), - [Log] = cuttlefish_lager_test_backend:get_logs(), + [Log] = cuttlefish_test_logging:get_logs(), ?assertMatch({match, _}, re:run(Log, "{level, intermediate} has been deprecated. Use 'hidden' or '{hidden, true}'")), ok. generate_element_hidden_test() -> - cuttlefish_lager_test_backend:bounce(warning), + _ = cuttlefish_test_logging:set_up(), + _ = cuttlefish_test_logging:bounce(warning), assert_no_output(hidden), assert_no_output({hidden, true}), - ?assertEqual([], cuttlefish_lager_test_backend:get_logs()), + ?assertEqual([], cuttlefish_test_logging:get_logs()), + ok. + +included_file_test() -> + Conf = file("test/include_file.conf"), + ?assertEqual(lists:sort([ + {["ring_size"],"32"}, + {["anti_entropy"],"debug"}, + {["log","error","file"],"/var/log/error.log"}, + {["log","console","file"],"/var/log/console.log"}, + {["log","syslog"],"on"}, + {["listener","http","internal"],"127.0.0.1:8098"}, + {["listener","http","external"],"10.0.0.1:80"} + ]), lists:sort(Conf)), + ok. + +included_dir_test() -> + Conf = file("test/include_dir.conf"), + ?assertEqual(lists:sort([ + {["anti_entropy"],"debug"}, + {["log","error","file"],"/var/log/error.log"}, + {["log","console","file"],"/var/log/console.log"}, + {["ring_size"],"5"}, + {["rogue","option"],"42"}, + {["listener","http","internal"],"127.0.0.1:8098"}, + {["listener","http","external"],"10.0.0.1:80"}, + {["log","syslog"],"off"} + ]), lists:sort(Conf)), + ok. + +included_value_test() -> + Conf = file("test/included_value.conf"), + ?assertEqual(lists:sort([ + {["value1"], "42"}, + {["value2"], "43"}, + {["value3"], "42"}, + {["value4"], "multi\nline\nvalue"}, + {["value5"], "12.34"}, + {["value6"], "$v1 [$v2] $v3 $v4"} + ]), lists:sort(Conf)), ok. assert_no_output(Setting) -> diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_datatypes.erl erlang-cuttlefish-3.0.1/src/cuttlefish_datatypes.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_datatypes.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_datatypes.erl 2021-03-26 15:47:17.000000000 +0000 @@ -23,7 +23,6 @@ -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. -type datatype() :: integer | @@ -36,11 +35,13 @@ {flag, {atom(), term()}, {atom(), term()}} | {enum, [atom()]} | ip | + fqdn | {duration, cuttlefish_duration:time_unit() } | bytesize | {percent, integer} | {percent, float} | - float. + float | + {list, datatype()}. -type extended() :: { integer, integer() } | { string, string() } | { file, file:filename() } | @@ -76,6 +77,7 @@ is_supported(atom) -> true; is_supported({enum, E}) when is_list(E) -> true; is_supported(ip) -> true; +is_supported(fqdn) -> true; is_supported({duration, f}) -> true; is_supported({duration, w}) -> true; is_supported({duration, d}) -> true; @@ -87,6 +89,11 @@ is_supported({percent, integer}) -> true; is_supported({percent, float}) -> true; is_supported(float) -> true; +is_supported({list, {list, _}}) -> + % lists of lists are not supported + false; +is_supported({list, ListDatatype}) -> + is_supported(ListDatatype); is_supported(_) -> false. -spec is_extended(any()) -> boolean(). @@ -97,6 +104,8 @@ is_extended({directory, D}) when is_list(D) -> true; is_extended({ip, {IP, Port}}) when is_list(IP) andalso is_integer(Port) -> true; is_extended({ip, StringIP}) when is_list(StringIP) -> true; +is_extended({fqdn, {FQDN, Port}}) when is_list(FQDN) andalso is_integer(Port) -> true; +is_extended({fqdn, StringFQDN}) when is_list(StringFQDN) -> true; is_extended({{duration, f}, D}) when is_list(D) -> true; is_extended({{duration, w}, D}) when is_list(D) -> true; is_extended({{duration, d}, D}) when is_list(D) -> true; @@ -117,6 +126,7 @@ extended_from({file, _}) -> file; extended_from({directory, _}) -> directory; extended_from({ip, _}) -> ip; +extended_from({fqdn, _}) -> fqdn; extended_from({{duration, Unit}, _}) -> {duration, Unit}; extended_from({bytesize, _}) -> bytesize; extended_from({{percent, integer}, _}) -> {percent, integer}; @@ -150,6 +160,9 @@ to_string({IP, Port}, ip) when is_list(IP), is_integer(Port) -> IP ++ ":" ++ integer_to_list(Port); to_string(IPString, ip) when is_list(IPString) -> IPString; +to_string({FQDN, Port}, fqdn) when is_list(FQDN), is_integer(Port) -> FQDN ++ ":" ++ integer_to_list(Port); +to_string(FQDNString, fqdn) when is_list(FQDNString) -> FQDNString; + to_string(Enum, {enum, _}) when is_list(Enum) -> Enum; to_string(Enum, {enum, _}) when is_atom(Enum) -> atom_to_list(Enum); @@ -212,6 +225,10 @@ from_string(String, ip) when is_list(String) -> from_string_to_ip(String, lists:split(string:rchr(String, $:), String)); +from_string({FQDN, Port}, fqdn) when is_list(FQDN), is_integer(Port) -> {FQDN, Port}; +from_string(String, fqdn) when is_list(String) -> + from_string_to_fqdn(String, lists:split(string:rchr(String, $:), String)); + from_string(Duration, {duration, _}) when is_integer(Duration) -> Duration; from_string(Duration, {duration, Unit}) when is_list(Duration) -> cuttlefish_duration:parse(Duration, Unit); @@ -256,6 +273,12 @@ _:_ -> {error, {conversion, {String, float}}} end; +from_string(List, {list, DataType}) when is_list(List) -> + lists:map(fun(El) -> + from_string(string:trim(El), DataType) + end, + string:split(List, ",", all)); + from_string(Thing, InvalidDatatype) -> {error, {type, {Thing, InvalidDatatype}}}. @@ -283,6 +306,17 @@ ip_conversions(_String, IPStr, {ok, _}, Port) -> {IPStr, Port}. +fqdn_conversions(String, _FQDNStr, nomatch, _Port) -> + {error, {conversion, {String, 'FQDN'}}}; +fqdn_conversions(String, _FQDNStr, _, undefined) -> + {error, {conversion, {String, 'FQDN'}}}; +fqdn_conversions(_String, FQDNStr, {match, _}, Port) -> + {FQDNStr, Port}. + +validate_fqdn(Str) -> + %% inspired by https://regexr.com/3g5j0, amended to disallow [:space:] + re:run(Str, "^(?!:\/\/)(?=[^[:space:]]{1,255}$)((.{1,63}\.){1,127}(?![0-9]*$)[a-z0-9-]+\.?)$"). + droplast(List) -> lists:sublist(List, length(List)-1). @@ -295,6 +329,12 @@ IP = droplast(IpPlusColon), ip_conversions(String, IP, inet:parse_address(IP), port_to_integer(PortString)). +from_string_to_fqdn(String, {[], String}) -> + {error, {conversion, {String, 'FQDN'}}}; +from_string_to_fqdn(String, {FQDNPlusColon, PortString}) -> + FQDN = droplast(FQDNPlusColon), + fqdn_conversions(String, FQDN, validate_fqdn(FQDN), port_to_integer(PortString)). + -ifdef(TEST). @@ -349,6 +389,8 @@ ?assertEqual("32", to_string("32", {integer, 32})), ?assertEqual("127.0.0.1:8098", to_string("127.0.0.1:8098", {ip, "127.0.0.1:8098"})), ?assertEqual("127.0.0.1:8098", to_string({"127.0.0.1", 8098}, {ip, {"127.0.0.1", 8098}})), + ?assertEqual("example.com:8098", to_string("example.com:8098", {fqdn, "example.com:8098"})), + ?assertEqual("example.com:8098", to_string({"example.com", 8098}, {fqdn, {"example.com", 8098}})), ?assertEqual("string", to_string("string", {string, "string"})), ?assertEqual("1w", to_string("1w", {{duration, s}, "1w"})), ?assertEqual("1w", to_string(604800000, {{duration, ms}, "1w"})), @@ -406,6 +448,38 @@ BadIPs), ok. +from_string_fqdn_test() -> + ?assertEqual({"fqdn.com", 8098}, from_string("fqdn.com:8098", fqdn)), + ?assertEqual( + {"f.q.d.n.com", 8098}, + from_string("f.q.d.n.com:8098", fqdn)), + ?assertEqual( + {"fqdn.com.", 8098}, + from_string("fqdn.com.:8098", fqdn)), + ?assertEqual( + {"FqDn.com", 8098}, + from_string("FqDn.com:8098", fqdn)), + ?assertEqual( + {"ec2-35-160-210-253.us-west-2-.compute.amazonaws.com", 1}, + from_string("ec2-35-160-210-253.us-west-2-.compute.amazonaws.com:1", fqdn)), + + BadFQDNs = [ + "This is not an fqdn:80", + "This.is not.an.fqdn:80", + "", + "127.0.0.1:80", + "fqdn.com", %% No port + "fqdn.com:-5", + "fqdn.com:80:81" + ], + + lists:foreach(fun(Bad) -> + ?assertEqual({error, {conversion, {Bad, 'FQDN'}}}, + from_string(Bad, fqdn)) + end, + BadFQDNs), + ok. + from_string_enum_test() -> ?assertEqual("\"a\" is not a valid enum value, acceptable values are: b, c", ?XLATE(from_string(a, {enum, [b, c]}))), ?assertEqual(true, from_string("true", {enum, [true, false]})), @@ -452,6 +526,27 @@ from_string_string_test() -> ?assertEqual("string", from_string("string", string)). +from_string_string_list_test() -> + %% more examples in the the cuttlefish_duration tests + ?assertEqual(["v1", "v2", "v3"], from_string("v1, v2,v3", {list, string})), + ok. + +from_string_integer_list_test() -> + %% more examples in the the cuttlefish_duration tests + ?assertEqual([1, 2, 3], from_string("1, 2,3", {list, integer})), + ok. + +from_string_atom_list_test() -> + %% more examples in the the cuttlefish_duration tests + ?assertEqual([a, b, c], from_string("a, b,c", {list, atom})), + ok. + +from_string_string_in_integer_list_test() -> + %% more examples in the the cuttlefish_duration tests + ?assertEqual([{error, {conversion, {"a", integer}}}, 1, 2], + from_string("a, 1,2", {list, integer})), + ok. + from_string_unsupported_datatype_test() -> ?assertEqual("Tried to convert \"string\" but invalid datatype: unsupported_datatype", ?XLATE(from_string("string", unsupported_datatype))). @@ -472,6 +567,8 @@ ?assert(is_supported({duration, s})), ?assert(is_supported({duration, ms})), ?assert(is_supported(bytesize)), + ?assert(is_supported({list, string})), + ?assert(not(is_supported({list, {list, string}}))), ?assert(not(is_supported(some_unsupported_type))), ok. diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_duration.erl erlang-cuttlefish-3.0.1/src/cuttlefish_duration.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_duration.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_duration.erl 2021-03-26 15:47:17.000000000 +0000 @@ -28,7 +28,6 @@ -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. -export([parse/1, parse/2, to_string/2]). diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_duration_parse.erl erlang-cuttlefish-3.0.1/src/cuttlefish_duration_parse.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_duration_parse.erl 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_duration_parse.erl 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1,343 @@ +-module(cuttlefish_duration_parse). +-export([parse/1,file/1]). +-define(p_charclass,true). +-define(p_choose,true). +-define(p_one_or_more,true). +-define(p_scan,true). +-define(p_seq,true). +-define(p_string,true). +-define(p_zero_or_more,true). + + + + +%% ------------------------------------------------------------------- +%% +%% cuttlefish_duration_parse: parses duration strings +%% +%% Copyright (c) 2014 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided 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. +%% +%% ------------------------------------------------------------------- + +-include("cuttlefish_duration.hrl"). + +-define(FLATTEN(S), binary_to_list(iolist_to_binary(S))). + +-spec file(file:name()) -> any(). +file(Filename) -> + AbsFilename = filename:absname(Filename), + case erl_prim_loader:get_file(AbsFilename) of + {ok, Bin, _} -> parse(Bin); + error -> {error, undefined} + end. + +-spec parse(binary() | list()) -> any(). +parse(List) when is_list(List) -> parse(list_to_binary(List)); +parse(Input) when is_binary(Input) -> + _ = setup_memo(), + Result = case 'duration'(Input,{{line,1},{column,1}}) of + {AST, <<>>, _Index} -> AST; + Any -> Any + end, + release_memo(), Result. + +-spec 'duration'(input(), index()) -> parse_result(). +'duration'(Input, Index) -> + p(Input, Index, 'duration', fun(I,D) -> (p_one_or_more(fun 'duration_segment'/2))(I,D) end, fun(Node, _Idx) ->lists:sum(Node) end). + +-spec 'duration_segment'(input(), index()) -> parse_result(). +'duration_segment'(Input, Index) -> + p(Input, Index, 'duration_segment', fun(I,D) -> (p_seq([p_choose([fun 'float'/2, fun 'integer'/2]), fun 'unit'/2]))(I,D) end, fun(Node, _Idx) -> + [Amount, Span] = Node, + {Span, Multiplier} = lists:keyfind(Span, 1, ?MULTIPLIERS), + Amount * Multiplier + end). + +-spec 'integer'(input(), index()) -> parse_result(). +'integer'(Input, Index) -> + p(Input, Index, 'integer', fun(I,D) -> (p_seq([p_charclass(<<"[1-9]">>), p_zero_or_more(p_charclass(<<"[0-9]">>))]))(I,D) end, fun(Node, _Idx) ->list_to_integer(?FLATTEN(Node)) end). + +-spec 'unit'(input(), index()) -> parse_result(). +'unit'(Input, Index) -> + p(Input, Index, 'unit', fun(I,D) -> (p_choose([p_string(<<"f">>), p_string(<<"w">>), p_string(<<"d">>), p_string(<<"h">>), p_string(<<"ms">>), p_string(<<"m">>), p_string(<<"s">>)]))(I,D) end, fun(Node, _Idx) ->binary_to_atom(Node, latin1) end). + +-spec 'float'(input(), index()) -> parse_result(). +'float'(Input, Index) -> + p(Input, Index, 'float', fun(I,D) -> (p_choose([p_seq([p_one_or_more(p_charclass(<<"[0-9]">>)), p_string(<<".">>), p_one_or_more(p_charclass(<<"[0-9]">>))]), p_seq([p_string(<<".">>), p_one_or_more(p_charclass(<<"[0-9]">>))])]))(I,D) end, fun(Node, _Idx) -> + case Node of + [<<".">>, Mantissa] -> + list_to_float(?FLATTEN(["0.", Mantissa])); + _ -> + list_to_float(?FLATTEN(Node)) + end + end). + + + +-file("peg_includes.hrl", 1). +-type index() :: {{line, pos_integer()}, {column, pos_integer()}}. +-type input() :: binary(). +-type parse_failure() :: {fail, term()}. +-type parse_success() :: {term(), input(), index()}. +-type parse_result() :: parse_failure() | parse_success(). +-type parse_fun() :: fun((input(), index()) -> parse_result()). +-type xform_fun() :: fun((input(), index()) -> term()). + +-spec p(input(), index(), atom(), parse_fun(), xform_fun()) -> parse_result(). +p(Inp, StartIndex, Name, ParseFun, TransformFun) -> + case get_memo(StartIndex, Name) of % See if the current reduction is memoized + {ok, Memo} -> %Memo; % If it is, return the stored result + Memo; + _ -> % If not, attempt to parse + Result = case ParseFun(Inp, StartIndex) of + {fail,_} = Failure -> % If it fails, memoize the failure + Failure; + {Match, InpRem, NewIndex} -> % If it passes, transform and memoize the result. + Transformed = TransformFun(Match, StartIndex), + {Transformed, InpRem, NewIndex} + end, + memoize(StartIndex, Name, Result), + Result + end. + +-spec setup_memo() -> ets:tid(). +setup_memo() -> + put({parse_memo_table, ?MODULE}, ets:new(?MODULE, [set])). + +-spec release_memo() -> true. +release_memo() -> + ets:delete(memo_table_name()). + +-spec memoize(index(), atom(), parse_result()) -> true. +memoize(Index, Name, Result) -> + Memo = case ets:lookup(memo_table_name(), Index) of + [] -> []; + [{Index, Plist}] -> Plist + end, + ets:insert(memo_table_name(), {Index, [{Name, Result}|Memo]}). + +-spec get_memo(index(), atom()) -> {ok, term()} | {error, not_found}. +get_memo(Index, Name) -> + case ets:lookup(memo_table_name(), Index) of + [] -> {error, not_found}; + [{Index, Plist}] -> + case proplists:lookup(Name, Plist) of + {Name, Result} -> {ok, Result}; + _ -> {error, not_found} + end + end. + +-spec memo_table_name() -> ets:tid(). +memo_table_name() -> + get({parse_memo_table, ?MODULE}). + +-ifdef(p_eof). +-spec p_eof() -> parse_fun(). +p_eof() -> + fun(<<>>, Index) -> {eof, [], Index}; + (_, Index) -> {fail, {expected, eof, Index}} end. +-endif. + +-ifdef(p_optional). +-spec p_optional(parse_fun()) -> parse_fun(). +p_optional(P) -> + fun(Input, Index) -> + case P(Input, Index) of + {fail,_} -> {[], Input, Index}; + {_, _, _} = Success -> Success + end + end. +-endif. + +-ifdef(p_not). +-spec p_not(parse_fun()) -> parse_fun(). +p_not(P) -> + fun(Input, Index)-> + case P(Input,Index) of + {fail,_} -> + {[], Input, Index}; + {Result, _, _} -> {fail, {expected, {no_match, Result},Index}} + end + end. +-endif. + +-ifdef(p_assert). +-spec p_assert(parse_fun()) -> parse_fun(). +p_assert(P) -> + fun(Input,Index) -> + case P(Input,Index) of + {fail,_} = Failure-> Failure; + _ -> {[], Input, Index} + end + end. +-endif. + +-ifdef(p_seq). +-spec p_seq([parse_fun()]) -> parse_fun(). +p_seq(P) -> + fun(Input, Index) -> + p_all(P, Input, Index, []) + end. + +-spec p_all([parse_fun()], input(), index(), [term()]) -> parse_result(). +p_all([], Inp, Index, Accum ) -> {lists:reverse( Accum ), Inp, Index}; +p_all([P|Parsers], Inp, Index, Accum) -> + case P(Inp, Index) of + {fail, _} = Failure -> Failure; + {Result, InpRem, NewIndex} -> p_all(Parsers, InpRem, NewIndex, [Result|Accum]) + end. +-endif. + +-ifdef(p_choose). +-spec p_choose([parse_fun()]) -> parse_fun(). +p_choose(Parsers) -> + fun(Input, Index) -> + p_attempt(Parsers, Input, Index, none) + end. + +-spec p_attempt([parse_fun()], input(), index(), none | parse_failure()) -> parse_result(). +p_attempt([], _Input, _Index, Failure) -> Failure; +p_attempt([P|Parsers], Input, Index, FirstFailure)-> + case P(Input, Index) of + {fail, _} = Failure -> + case FirstFailure of + none -> p_attempt(Parsers, Input, Index, Failure); + _ -> p_attempt(Parsers, Input, Index, FirstFailure) + end; + Result -> Result + end. +-endif. + +-ifdef(p_zero_or_more). +-spec p_zero_or_more(parse_fun()) -> parse_fun(). +p_zero_or_more(P) -> + fun(Input, Index) -> + p_scan(P, Input, Index, []) + end. +-endif. + +-ifdef(p_one_or_more). +-spec p_one_or_more(parse_fun()) -> parse_fun(). +p_one_or_more(P) -> + fun(Input, Index)-> + Result = p_scan(P, Input, Index, []), + case Result of + {[_|_], _, _} -> + Result; + _ -> + {fail, {expected, Failure, _}} = P(Input,Index), + {fail, {expected, {at_least_one, Failure}, Index}} + end + end. +-endif. + +-ifdef(p_label). +-spec p_label(atom(), parse_fun()) -> parse_fun(). +p_label(Tag, P) -> + fun(Input, Index) -> + case P(Input, Index) of + {fail,_} = Failure -> + Failure; + {Result, InpRem, NewIndex} -> + {{Tag, Result}, InpRem, NewIndex} + end + end. +-endif. + +-ifdef(p_scan). +-spec p_scan(parse_fun(), input(), index(), [term()]) -> {[term()], input(), index()}. +p_scan(_, <<>>, Index, Accum) -> {lists:reverse(Accum), <<>>, Index}; +p_scan(P, Inp, Index, Accum) -> + case P(Inp, Index) of + {fail,_} -> {lists:reverse(Accum), Inp, Index}; + {Result, InpRem, NewIndex} -> p_scan(P, InpRem, NewIndex, [Result | Accum]) + end. +-endif. + +-ifdef(p_string). +-spec p_string(binary()) -> parse_fun(). +p_string(S) -> + Length = erlang:byte_size(S), + fun(Input, Index) -> + try + <> = Input, + {S, Rest, p_advance_index(S, Index)} + catch + error:{badmatch,_} -> {fail, {expected, {string, S}, Index}} + end + end. +-endif. + +-ifdef(p_anything). +-spec p_anything() -> parse_fun(). +p_anything() -> + fun(<<>>, Index) -> {fail, {expected, any_character, Index}}; + (Input, Index) when is_binary(Input) -> + <> = Input, + {<>, Rest, p_advance_index(<>, Index)} + end. +-endif. + +-ifdef(p_charclass). +-spec p_charclass(string() | binary()) -> parse_fun(). +p_charclass(Class) -> + {ok, RE} = re:compile(Class, [unicode, dotall]), + fun(Inp, Index) -> + case re:run(Inp, RE, [anchored]) of + {match, [{0, Length}|_]} -> + {Head, Tail} = erlang:split_binary(Inp, Length), + {Head, Tail, p_advance_index(Head, Index)}; + _ -> {fail, {expected, {character_class, binary_to_list(Class)}, Index}} + end + end. +-endif. + +-ifdef(p_regexp). +-spec p_regexp(binary()) -> parse_fun(). +p_regexp(Regexp) -> + {ok, RE} = re:compile(Regexp, [unicode, dotall, anchored]), + fun(Inp, Index) -> + case re:run(Inp, RE) of + {match, [{0, Length}|_]} -> + {Head, Tail} = erlang:split_binary(Inp, Length), + {Head, Tail, p_advance_index(Head, Index)}; + _ -> {fail, {expected, {regexp, binary_to_list(Regexp)}, Index}} + end + end. +-endif. + +-ifdef(line). +-spec line(index() | term()) -> pos_integer() | undefined. +line({{line,L},_}) -> L; +line(_) -> undefined. +-endif. + +-ifdef(column). +-spec column(index() | term()) -> pos_integer() | undefined. +column({_,{column,C}}) -> C; +column(_) -> undefined. +-endif. + +-spec p_advance_index(input() | unicode:charlist() | pos_integer(), index()) -> index(). +p_advance_index(MatchedInput, Index) when is_list(MatchedInput) orelse is_binary(MatchedInput)-> % strings + lists:foldl(fun p_advance_index/2, Index, unicode:characters_to_list(MatchedInput)); +p_advance_index(MatchedInput, Index) when is_integer(MatchedInput) -> % single characters + {{line, Line}, {column, Col}} = Index, + case MatchedInput of + $\n -> {{line, Line+1}, {column, 1}}; + _ -> {{line, Line}, {column, Col+1}} + end. diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_effective.erl erlang-cuttlefish-3.0.1/src/cuttlefish_effective.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_effective.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_effective.erl 2021-03-26 15:47:17.000000000 +0000 @@ -27,7 +27,6 @@ -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. -spec build(cuttlefish_conf:conf(), cuttlefish_schema:schema(), [proplists:property()]) -> [string()]. diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_enum.erl erlang-cuttlefish-3.0.1/src/cuttlefish_enum.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_enum.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_enum.erl 2021-03-26 15:47:17.000000000 +0000 @@ -30,7 +30,6 @@ -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. -export([ diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish.erl erlang-cuttlefish-3.0.1/src/cuttlefish.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish.erl 2021-03-26 15:47:17.000000000 +0000 @@ -22,9 +22,10 @@ -module(cuttlefish). +-include_lib("kernel/include/logger.hrl"). + -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. -export([ @@ -124,7 +125,7 @@ %% being logged. -spec warn(iodata()) -> ok. warn(Str) -> - lager:warning(Str, []). + _ = ?LOG_WARNING(Str, []). -ifdef(TEST). diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_error.erl erlang-cuttlefish-3.0.1/src/cuttlefish_error.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_error.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_error.erl 2021-03-26 15:47:17.000000000 +0000 @@ -21,6 +21,8 @@ %% ------------------------------------------------------------------- -module(cuttlefish_error). +-include_lib("kernel/include/logger.hrl"). + -type error() :: {'error', {atom(), term()}}. -type errorlist() :: {'errorlist', [error()]}. -export_type([error/0, errorlist/0]). @@ -37,7 +39,6 @@ -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. %% We'll be calling this a lot from `xlate' @@ -178,11 +179,11 @@ print({error, ErrorTerm}) -> print(lists:flatten(xlate(ErrorTerm))); print(String) -> - case lager:error("~s", [String]) of - {error, lager_not_running} -> - io:format("~s~n", [String]), - ok; - ok -> ok + try + ?LOG_ERROR("~s", [String]) + catch _:_:_ -> + io:format("~s~n", [String]), + ok end. -ifdef(TEST). diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_escript.erl erlang-cuttlefish-3.0.1/src/cuttlefish_escript.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_escript.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_escript.erl 2021-03-26 15:47:17.000000000 +0000 @@ -21,13 +21,15 @@ %% ------------------------------------------------------------------- -module(cuttlefish_escript). +-define(LOGGER_HANDLER, default). -define(STDOUT(Str, Args), io:format(Str ++ "~n", Args)). -define(FORMAT(Str, Args), io_lib:format(Str, Args)). -export([main/1]). +-include_lib("kernel/include/logger.hrl"). + -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. cli_options() -> @@ -36,14 +38,19 @@ {help, $h, "help", undefined, "Print this usage page"}, {etc_dir, $e, "etc_dir", {string, "/etc"}, "etc dir"}, {dest_dir, $d, "dest_dir", string, "specifies the directory to write the config file to"}, - {dest_file, $f, "dest_file", {string, "app"}, "the file name to write"}, + {dest_file, $f, "dest_file", string, "the file name to write"}, {schema_dir, $s, "schema_dir", string, "a directory containing .schema files"}, + %% one or more schema file paths {schema_file, $i, "schema_file", string, "individual schema file, will be processed in command line order, after -s"}, - {conf_file, $c, "conf_file", string, "a cuttlefish conf file, multiple files allowed"}, - {app_config, $a, "app_config", string, "the advanced erlangy app.config"}, + %% one or more sysctl-style configuration file paths + {conf_file, $c, "conf_file", string, "a cuttlefish conf file path, multiple files allowed"}, + %% overrides advanced.config file path + {advanced_conf_file, $a, "advanced_conf_file", string, "the advanced config file path"}, {log_level, $l, "log_level", {string, "notice"}, "log level for cuttlefish output"}, {print_schema, $p, "print", undefined, "prints schema mappings on stderr"}, - {max_history, $m, "max_history", {integer, 3}, "the maximum number of generated config files to keep"} + {max_history, $m, "max_history", {integer, 3}, "the maximum number of generated config files to keep"}, + {silent, $t, "silent", {boolean, false}, "silent operation, no output"}, + {allow_extra, $x, "allow_extra", {boolean, false}, "don't fail if extra keys not belonging to a schema are found"} ]. %% LOL! I wanted this to be halt 0, but honestly, if this escript does anything @@ -68,8 +75,6 @@ %% @doc main method for generating erlang term config files main(Args) -> - _ = application:load(lager), - {Command, ParsedArgs, Extra} = parse_and_command(Args), SuggestedLogLevel = list_to_atom(proplists:get_value(log_level, ParsedArgs)), @@ -78,10 +83,22 @@ _ -> notice end, - application:set_env(lager, handlers, [{lager_stderr_backend, LogLevel}]), - application:set_env(lager, crash_log, false), - lager:start(), - lager:debug("Cuttlefish set to debug level logging"), + logger:set_primary_config(#{ + level => LogLevel + }), + {ok, LC0} = logger:get_handler_config(?LOGGER_HANDLER), + %% override logger formatter to match what 2.7.0 and earlier versions + %% used with Lager + LC1 = maps:update(formatter, + {logger_formatter, #{ + legacy_header => false, + single_line => true, + template => [time," [", level ,"] ", msg, "\n"] + }}, LC0), + logger:update_handler_config(?LOGGER_HANDLER, LC1), + + _ = ?LOG_DEBUG("Cuttlefish log level is set to ~s", [LogLevel]), + _ = ?LOG_DEBUG("Parsed arguments: ~p", [ParsedArgs]), case Command of help -> @@ -98,7 +115,7 @@ %% This shows the effective configuration, including defaults effective(ParsedArgs) -> - lager:debug("cuttlefish `effective`", []), + _ = ?LOG_DEBUG("cuttlefish `effective`", []), EtcDir = proplists:get_value(etc_dir, ParsedArgs), %% Should we even show this? @@ -107,15 +124,16 @@ case {AppConfigExists, VMArgsExists} of {false, false} -> - AdvancedConfigFile = filename:join(EtcDir, "advanced.config"), + AdvancedConfigFile = proplists:get_value(advanced_conf_file, ParsedArgs, filename:join(EtcDir, "advanced.config")), + _ = ?LOG_DEBUG("Will look for advanced.config at '~s'", [AdvancedConfigFile]), AdvConfig = case filelib:is_file(AdvancedConfigFile) of true -> - lager:debug("~s/advanced.config detected, overlaying proplists", [EtcDir]), + _ = ?LOG_DEBUG("~s detected, overlaying proplists", [AdvancedConfigFile]), case file:consult(AdvancedConfigFile) of {ok, [AdvancedConfig]} -> AdvancedConfig; {error, Error} -> - lager:error("Error parsing advanced.config: ~s", [file:format_error(Error)]), + _ = ?LOG_ERROR("Error parsing advanced.config: ~s", [file:format_error(Error)]), stop_deactivate() end; _ -> @@ -155,7 +173,7 @@ describe(ParsedArgs, [Query|_]) when is_list(Query) -> QDef = cuttlefish_variable:tokenize(Query), - lager:debug("cuttlefish describe '~s'", [Query]), + _ = ?LOG_DEBUG("cuttlefish describe '~s'", [Query]), {_, Mappings, _} = load_schema(ParsedArgs), FindResults = fun(QueryVar) -> @@ -242,24 +260,28 @@ %% even though cuttlefish is awesome FilesToUse = case {AppConfigExists, VMArgsExists} of {true, true} -> - lager:info("~s and ~s exists, disabling cuttlefish.", [ExistingAppConfigName, ExistingVMArgsName]), - lager:info("If you'd like to know more about cuttlefish, check your local library!", []), - lager:info(" or see http://github.com/basho/cuttlefish", []), + _ = ?LOG_INFO("~s and ~s exists, disabling cuttlefish.", [ExistingAppConfigName, ExistingVMArgsName]), + _ = ?LOG_INFO("If you'd like to know more about cuttlefish, check your local library!", []), + _ = ?LOG_INFO(" or see http://github.com/Kyorai/cuttlefish", []), {ExistingAppConfigName, ExistingVMArgsName}; {true, false} -> - lager:info("~s exists, generating vm.args", [ExistingAppConfigName]), + _ = ?LOG_INFO("~s exists, generating vm.args", [ExistingAppConfigName]), {_, NewVMArgs} = engage_cuttlefish(ParsedArgs), {ExistingAppConfigName, NewVMArgs}; {false, true} -> - lager:info("~s exists, generating app.config", [ExistingVMArgsName]), + _ = ?LOG_INFO("~s exists, generating app.config", [ExistingVMArgsName]), {NewAppConfig, _} = engage_cuttlefish(ParsedArgs), {NewAppConfig, ExistingVMArgsName}; _ -> - lager:info("No app.config or vm.args detected in ~s, activating cuttlefish", [EtcDir]), + _ = ?LOG_INFO("No app.config or vm.args detected in ~s, activating cuttlefish", [EtcDir]), engage_cuttlefish(ParsedArgs) end, - case FilesToUse of + Silent = proplists:get_value(silent, ParsedArgs, false), + case Silent orelse FilesToUse of + true -> + %% user requested for silent operation, ie. not cli args + stop_ok(); %% this is nice and all, but currently all error paths of engage_cuttlefish end with %% stop_deactivate() hopefully factor that to be cleaner. error -> @@ -286,10 +308,10 @@ SortedSchemaFiles = lists:sort(fun(A,B) -> A < B end, SchemaFiles), case length(SortedSchemaFiles) of 0 -> - lager:debug("No Schema files found in specified", []), + _ = ?LOG_DEBUG("No Schema files found in specified", []), stop_deactivate(); _ -> - lager:debug("SchemaFiles: ~p", [SortedSchemaFiles]) + _ = ?LOG_DEBUG("SchemaFiles: ~p", [SortedSchemaFiles]) end, Schema = cuttlefish_schema:files(SortedSchemaFiles), @@ -303,10 +325,10 @@ load_conf(ParsedArgs) -> ConfFiles = proplists:get_all_values(conf_file, ParsedArgs), - lager:debug("ConfFiles: ~p", [ConfFiles]), + _ = ?LOG_DEBUG("ConfFiles: ~p", [ConfFiles]), case cuttlefish_conf:files(ConfFiles) of {errorlist, Errors} -> - _ = [ lager:error(cuttlefish_error:xlate(E)) || + _ = [ _ = ?LOG_ERROR(cuttlefish_error:xlate(E)) || {error, E} <- Errors], stop_deactivate(), {errorlist, Errors}; @@ -329,7 +351,7 @@ ok -> AbsoluteDestPath; {error, E} -> - lager:error( + _ = ?LOG_ERROR( "Error creating ~s: ~s", [AbsoluteDestPath, file:format_error(E)]), error @@ -346,39 +368,38 @@ Path -> Path end, - Date = calendar:local_time(), - - DestinationFilename = filename_maker(proplists:get_value(dest_file, ParsedArgs), Date, "config"), + DestinationFilename = filename_maker(proplists:get_value(dest_file, ParsedArgs, "app"), "config"), Destination = filename:join(AbsPath, DestinationFilename), - DestinationVMArgsFilename = filename_maker("vm", Date, "args"), + DestinationVMArgsFilename = filename_maker(proplists:get_value(dest_file, ParsedArgs, "vm"), "args"), DestinationVMArgs = filename:join(AbsPath, DestinationVMArgsFilename), - lager:debug("Generating config in: ~p", [Destination]), + _ = ?LOG_DEBUG("Generating config in: ~p", [Destination]), + _ = ?LOG_DEBUG("Generating vm.args in: ~p", [DestinationVMArgs]), Schema = load_schema(ParsedArgs), - Conf = load_conf(ParsedArgs), - NewConfig = case cuttlefish_generator:map(Schema, Conf) of + NewConfig = case cuttlefish_generator:map(Schema, Conf, ParsedArgs) of {error, Phase, {errorlist, Errors}} -> - lager:error("Error generating configuration in phase ~s", [Phase]), + _ = ?LOG_ERROR("Error generating configuration in phase ~s", [Phase]), _ = [ cuttlefish_error:print(E) || E <- Errors], stop_deactivate(); ValidConfig -> ValidConfig end, - AdvancedConfigFile = filename:join(EtcDir, "advanced.config"), + AdvancedConfigFile = proplists:get_value(advanced_conf_file, ParsedArgs, filename:join(EtcDir, "advanced.config")), + _ = ?LOG_DEBUG("AdvancedConfigFile: ~p", [AdvancedConfigFile]), FinalConfig = case filelib:is_file(AdvancedConfigFile) of true -> - lager:info("~s/advanced.config detected, overlaying proplists", [EtcDir]), + _ = ?LOG_INFO("advanced config file is detected at ~s, overlaying proplists", [AdvancedConfigFile]), case file:consult(AdvancedConfigFile) of {ok, [AdvancedConfig]} -> cuttlefish_advanced:overlay(NewConfig, AdvancedConfig); {ok, OtherTerms} -> - lager:error("Error parsing ~s, incorrect format: ~p", [AdvancedConfigFile, OtherTerms]), + _ = ?LOG_ERROR("Error parsing ~s, incorrect format: ~p", [AdvancedConfigFile, OtherTerms]), stop_deactivate(); {error, Error} -> - lager:error("Error parsing ~s: ~s", [AdvancedConfigFile, file:format_error(Error)]), + _ = ?LOG_ERROR("Error parsing ~s: ~s", [AdvancedConfigFile, file:format_error(Error)]), stop_deactivate() end; _ -> @@ -391,16 +412,16 @@ error; _ -> FinalAppConfig = proplists:delete(vm_args, FinalConfig), - FinalVMArgs = cuttlefish_vmargs:stringify(proplists:get_value(vm_args, FinalConfig)), + FinalVMArgs = cuttlefish_vmargs:stringify(proplists:get_value(vm_args, FinalConfig, [])), %% Prune excess files MaxHistory = proplists:get_value(max_history, ParsedArgs, 3) - 1, prune(Destination, MaxHistory), prune(DestinationVMArgs, MaxHistory), - case { file:write_file(Destination, io_lib:fwrite("~p.\n",[FinalAppConfig])), - file:write_file(DestinationVMArgs, string:join(FinalVMArgs, "\n"))} of - {ok, ok} -> + case {maybe_write_file(Destination, "~p.\n", FinalAppConfig), + maybe_write_file(DestinationVMArgs, "~s", string:join(FinalVMArgs, "\n"))} of + {ok, ok} -> {Destination, DestinationVMArgs}; {Err1, Err2} -> maybe_log_file_error(Destination, Err1), @@ -410,6 +431,15 @@ end. +-spec maybe_write_file(Filename :: string(), + Format :: string(), + Data :: string()) -> ok | {error, file:posix() | badarg | terminated | system_limit}. +maybe_write_file(_, _, []) -> + % nothing to write, write nothing + ok; +maybe_write_file(Filename, Format, Data) -> + file:write_file(Filename, io_lib:fwrite(Format, [Data])). + -spec prune(file:name_all(), integer()) -> ok. prune(Filename, MaxHistory) -> %% A Filename comes in /Abs/Path/To/something.YYYY.MM.DD.HH.mm.SS.ext @@ -431,7 +461,7 @@ case file:delete(File) of ok -> ok; {error, Reason} -> - lager:error("Could not delete ~s, ~p", [File, Reason]) + _ = ?LOG_ERROR("Could not delete ~s, ~p", [File, Reason]) end, delete(Files, MaxHistory). @@ -445,18 +475,25 @@ maybe_log_file_error(_, ok) -> ok; maybe_log_file_error(Filename, {error, Reason}) -> - lager:error("Error writing ~s: ~s", [Filename, file:format_error(Reason)]), + _ = ?LOG_ERROR("Error writing ~s: ~s", [Filename, file:format_error(Reason)]), ok. -spec check_existence(string(), string()) -> {boolean(), string()}. check_existence(EtcDir, Filename) -> FullName = filename:join(EtcDir, Filename), %% Barfolomew Exists = filelib:is_file(FullName), - lager:info("Checking ~s exists... ~p", [FullName, Exists]), + _ = ?LOG_INFO("Checking ~s exists... ~p", [FullName, Exists]), {Exists, FullName}. -filename_maker(Filename, Date, Extension) -> - {{Y, M, D}, {HH, MM, SS}} = Date, +filename_maker(Filename, Extension) -> + case length(string:tokens(Filename, ".")) of + 1 -> filename_maker(add_suffix, Filename, Extension); + _ -> filename_maker(no_suffix, Filename, Extension) + end. + +filename_maker(no_suffix, Filename, _Extension) -> Filename; +filename_maker(add_suffix, Filename, Extension) -> + {{Y, M, D}, {HH, MM, SS}} = calendar:local_time(), _DestinationFilename = io_lib:format("~s.~p.~s.~s.~s.~s.~s.~s", [Filename, @@ -477,7 +514,7 @@ end. print_schema(Schema) -> - lager:info("Printing Schema Mappings"), + _ = ?LOG_INFO("Printing Schema Mappings"), {_, Mappings, _} = Schema, {Max, ListOfMappings} = lists:foldr( diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_flag.erl erlang-cuttlefish-3.0.1/src/cuttlefish_flag.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_flag.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_flag.erl 2021-03-26 15:47:17.000000000 +0000 @@ -25,7 +25,6 @@ -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. -export([ diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_generator.erl erlang-cuttlefish-3.0.1/src/cuttlefish_generator.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_generator.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_generator.erl 2021-03-26 15:47:17.000000000 +0000 @@ -21,9 +21,10 @@ %% ------------------------------------------------------------------- -module(cuttlefish_generator). +-include_lib("kernel/include/logger.hrl"). + -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. -define(FMT(F,A), lists:flatten(io_lib:format(F,A))). @@ -33,13 +34,19 @@ -define(LSUBLEN, 2). -define(RSUBLEN, 1). --export([map/2, find_mapping/2, add_defaults/2, minimal_map/2]). +-export([map/2, map/3, find_mapping/2, add_defaults/2, minimal_map/2]). -spec map(cuttlefish_schema:schema(), cuttlefish_conf:conf()) -> [proplists:property()] | {error, atom(), cuttlefish_error:errorlist()}. map(Schema, Config) -> - map_add_defaults(Schema, Config). + map(Schema, Config, []). + +-spec map(cuttlefish_schema:schema(), cuttlefish_conf:conf(), [proplists:property()]) -> + [proplists:property()] | + {error, atom(), cuttlefish_error:errorlist()}. +map(Schema, Config, ParsedArgs) -> + map_add_defaults(Schema, Config, ParsedArgs). %% @doc Generates an Erlang config that only includes the settings %% encompassed by the passed Config, excluding defaults from the @@ -53,7 +60,7 @@ end, {[], sets:new()}, AllMappings), RestrictedTranslations = [ T || T <- AllTranslations, sets:is_element(cuttlefish_translation:mapping(T), MappingKeys)], - map({RestrictedTranslations,RestrictedMappings,V}, Config). + map({RestrictedTranslations,RestrictedMappings,V}, Config, []). restrict_mappings(M, {Mappings, Keys}, ConfigKeys) -> case sets:is_element(cuttlefish_mapping:variable(M), ConfigKeys) of @@ -63,42 +70,42 @@ {Mappings, Keys} end. --spec map_add_defaults(cuttlefish_schema:schema(), cuttlefish_conf:conf()) -> +-spec map_add_defaults(cuttlefish_schema:schema(), cuttlefish_conf:conf(), [proplists:property()]) -> [proplists:property()] | {error, atom(), cuttlefish_error:errorlist()}. -map_add_defaults({_, Mappings, _} = Schema, Config) -> +map_add_defaults({_, Mappings, _} = Schema, Config, ParsedArgs) -> %% Config at this point is just what's in the .conf file. %% add_defaults/2 rolls the default values in from the schema - lager:debug("Adding Defaults"), + _ = ?LOG_DEBUG("Adding Defaults"), DConfig = add_defaults(Config, Mappings), case cuttlefish_error:errorlist_maybe(DConfig) of {errorlist, EList} -> {error, add_defaults, {errorlist, EList}}; _ -> - map_value_sub(Schema, DConfig) + map_value_sub(Schema, DConfig, ParsedArgs) end. --spec map_value_sub(cuttlefish_schema:schema(), cuttlefish_conf:conf()) -> +-spec map_value_sub(cuttlefish_schema:schema(), cuttlefish_conf:conf(), [proplists:property()]) -> [proplists:property()] | {error, atom(), cuttlefish_error:errorlist()}. -map_value_sub(Schema, Config) -> - lager:debug("Right Hand Side Substitutions"), +map_value_sub(Schema, Config, ParsedArgs) -> + _ = ?LOG_DEBUG("Right Hand Side Substitutions"), case value_sub(Config) of {SubbedConfig, []} -> - map_transform_datatypes(Schema, SubbedConfig); + map_transform_datatypes(Schema, SubbedConfig, ParsedArgs); {_, EList} -> {error, rhs_subs, {errorlist, EList}} end. --spec map_transform_datatypes(cuttlefish_schema:schema(), cuttlefish_conf:conf()) -> +-spec map_transform_datatypes(cuttlefish_schema:schema(), cuttlefish_conf:conf(), [proplists:property()]) -> [proplists:property()] | {error, atom(), cuttlefish_error:errorlist()}. -map_transform_datatypes({_, Mappings, _} = Schema, DConfig) -> +map_transform_datatypes({_, Mappings, _} = Schema, DConfig, ParsedArgs) -> %% Everything in DConfig is of datatype "string", %% transform_datatypes turns them into other erlang terms %% based on the schema - lager:debug("Applying Datatypes"), - case transform_datatypes(DConfig, Mappings) of + _ = ?LOG_DEBUG("Applying Datatypes"), + case transform_datatypes(DConfig, Mappings, ParsedArgs) of {NewConf, []} -> map_validate(Schema, NewConf); {_, EList} -> @@ -110,7 +117,7 @@ {error, atom(), cuttlefish_error:errorlist()}. map_validate(Schema, Conf) -> %% Any more advanced validators - lager:debug("Validation"), + _ = ?LOG_DEBUG("Validation"), case cuttlefish_error:errorlist_maybe(run_validations(Schema, Conf)) of {errorlist, EList} -> {error, validation, {errorlist, EList}}; @@ -155,7 +162,7 @@ end, {[], {ordsets:new(),ordsets:new()}}, Mappings), - lager:debug("Applied 1:1 Mappings"), + _ = ?LOG_DEBUG("Applied 1:1 Mappings"), TranslationsToDrop = TranslationsToMaybeDrop -- TranslationsToKeep, {DirectMappings, TranslationsToDrop}. @@ -171,7 +178,7 @@ {DirectMappings, []}, Translations), case Errorlist of [] -> - lager:debug("Applied Translations"), + _ = ?LOG_DEBUG("Applied Translations"), Proplist; Es -> {error, apply_translations, {errorlist, Es}} @@ -186,7 +193,7 @@ false -> {XlatFun, XlatArgs} = prepare_translation_fun(Conf, Schema, Mapping, Xlat), - lager:debug("Running translation for ~s", [Mapping]), + _ = ?LOG_DEBUG("Running translation for ~s", [Mapping]), case try_apply_translation(Mapping, XlatFun, XlatArgs) of unset -> {Acc, Errors}; @@ -196,7 +203,7 @@ {Acc, [{error, Term}|Errors]} end; _ -> - lager:debug("~p in Translations to drop...", [Mapping]), + _ = ?LOG_DEBUG("~p in Translations to drop...", [Mapping]), {Acc, Errors} end end. @@ -295,7 +302,7 @@ %% If Match =:= true, do nothing, the value is set in the .conf file _ -> %% TODO: Handle with more style and grace - lager:error("Both fuzzy and strict match! should not happen"), + _ = ?LOG_ERROR("Both fuzzy and strict match! should not happen"), [{error, {map_multiple_match, VariableDef}}|Acc] end. @@ -388,14 +395,21 @@ -spec transform_datatypes( cuttlefish_conf:conf(), - [cuttlefish_mapping:mapping()] + [cuttlefish_mapping:mapping()], + [proplists:property()] ) -> {cuttlefish_conf:conf(), [cuttlefish_error:error()]}. -transform_datatypes(Conf, Mappings) -> +transform_datatypes(Conf, Mappings, ParsedArgs) -> lists:foldl( fun({Variable, Value}, {Acc, ErrorAcc}) -> + AllowExtra = proplists:get_value(allow_extra, ParsedArgs, false), %% Look up mapping from schema case find_mapping(Variable, Mappings) of - {error, _} -> + {error, _} when AllowExtra -> + %% user asked for us to tolerate variables + %% that are not present in the mapping so + %% do nothing here + {Acc, ErrorAcc}; + {error, _} -> %% So, this error message isn't so performant (s/o @argv0) %% but it shouldn't happen too often, and I think it's important %% to give users this feedback. @@ -403,15 +417,15 @@ %% It will prevent anything from starting, and will let you know %% that you're trying to set something that has no effect VarName = cuttlefish_variable:format(Variable), - lager:error("You've tried to set ~s, but there is no setting with that name.", [VarName]), - lager:error(" Did you mean one of these?"), + _ = ?LOG_ERROR("You've tried to set ~s, but there is no setting with that name.", [VarName]), + _ = ?LOG_ERROR(" Did you mean one of these?"), Possibilities = [ begin MapVarName = cuttlefish_variable:format(cuttlefish_mapping:variable(M)), {cuttlefish_util:levenshtein(VarName, MapVarName), MapVarName} end || M <- Mappings], Sorted = lists:sort(Possibilities), - _ = [ lager:error(" ~s", [T]) || {_, T} <- lists:sublist(Sorted, 3) ], + _ = [ _ = ?LOG_ERROR(" ~s", [T]) || {_, T} <- lists:sublist(Sorted, 3) ], {Acc, [ {error, {unknown_variable, VarName}} | ErrorAcc ]}; MappingRecord -> DTs = cuttlefish_mapping:datatype(MappingRecord), @@ -464,7 +478,7 @@ case head_sub(Value) of none -> {Value, Conf}; {sub, NextVar, {SubFront, SubBack}} -> - case proplists:get_value(NextVar, Conf) of + case subbed_value(NextVar, Conf) of undefined -> {error, {substitution_missing_config, {cuttlefish_variable:format(Var), @@ -487,6 +501,23 @@ value_sub(_Var, Value, Conf, _History) -> {Value, Conf}. +-spec subbed_value(Var :: cuttlefish_variable:variable(), + Conf :: cuttlefish_conf:conf()) -> undefined | string(). +subbed_value(Var, Conf) -> + case proplists:get_value(Var, Conf) of + undefined -> + % we couldn't find the var in the conf file, let's + % look at the environment and check for it + case os:getenv(cuttlefish_variable:format(Var)) of + false -> + undefined; + Value -> + Value + end; + Value -> + Value + end. + -spec head_sub(string()) -> none | {sub, cuttlefish_variable:variable(), {string(), string()}}. head_sub(Value) -> L = string:str(Value, ?LSUB), @@ -593,7 +624,7 @@ cuttlefish_mapping:variable(M)), cuttlefish_validator:description(V) }}, - lager:error(cuttlefish_error:xlate(Error)), + _ = ?LOG_ERROR(cuttlefish_error:xlate(Error)), {error, Error} end end || V <- Vs] @@ -667,7 +698,6 @@ ok. add_defaults_test() -> - %%lager:start(), Conf = [ %%{["a","b","c"], "override"}, %% Specifically left out. Uncomment line to break test, {["a","c","d"], "override"}, @@ -721,10 +751,9 @@ ok. map_test() -> - lager:start(), - Schema = cuttlefish_schema:file("../test/riak.schema"), + Schema = cuttlefish_schema:file("test/riak.schema"), - Conf = conf_parse:file("../test/riak.conf"), + Conf = conf_parse:file("test/riak.conf"), NewConfig = map(Schema, Conf), @@ -748,8 +777,7 @@ ok. minimal_map_test() -> - lager:start(), - Schema = cuttlefish_schema:file("../test/riak.schema"), + Schema = cuttlefish_schema:file("test/riak.schema"), Conf = [{["ring_size"], "32"}, {["anti_entropy"], "debug"}], NewConfig = minimal_map(Schema, Conf), @@ -796,7 +824,6 @@ ok. find_mapping_test() -> - lager:start(), Mappings = [ cuttlefish_mapping:parse({mapping, "variable.with.fixed.name", "", [{ default, 0}]}), cuttlefish_mapping:parse({mapping, "variable.with.$matched.name", "", [{ default, 1}]}) @@ -994,10 +1021,27 @@ Conf = [ {["conf", "other"], "string"} ], - NewConf = transform_datatypes(Conf, Mappings), + NewConf = transform_datatypes(Conf, Mappings, []), ?assertEqual({[], [{error, {unknown_variable, "conf.other"}}]}, NewConf), ok. +transform_datatypes_allowed_not_found_test() -> + Mappings = [ + cuttlefish_mapping:parse({ + mapping, + "conf.key", + "erlang.key", + [] + }) + ], + + Conf = [ + {["conf", "other"], "string"} + ], + NewConf = transform_datatypes(Conf, Mappings, [{allow_extra, true}]), + ?assertEqual({[], []}, NewConf), + ok. + validation_test() -> Pid = self(), @@ -1111,6 +1155,19 @@ {"b.c", "review all files"}}}]}}, AppConf). +value_env_sub_test() -> + os:putenv("ENV_TEST_VAL", "/a/b"), + Conf = [ + {["a","b","c"], "$(ENV_TEST_VAL)/c"}, + {["a","b"], "/b/a"} + ], + {NewConf, Errors} = value_sub(Conf), + os:unsetenv("ENV_TEST_VAL"), + ?assertEqual([], Errors), + ABC = proplists:get_value(["a","b","c"], NewConf), + ?assertEqual("/a/b/c", ABC), + ok. + value_sub_test() -> Conf = [ {["a","b","c"], "$(a.b)/c"}, diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_mapping.erl erlang-cuttlefish-3.0.1/src/cuttlefish_mapping.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_mapping.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_mapping.erl 2021-03-26 15:47:17.000000000 +0000 @@ -23,7 +23,6 @@ -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. -record(mapping, { diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_schema.erl erlang-cuttlefish-3.0.1/src/cuttlefish_schema.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_schema.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_schema.erl 2021-03-26 15:47:17.000000000 +0000 @@ -22,17 +22,18 @@ -module(cuttlefish_schema). --export([files/1, strings/1]). - -%% Exported for unit testing in other projects --export([merger/1, string_fun_factory/0]). +-include_lib("kernel/include/logger.hrl"). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). +-export([file/1]). +-endif. +-export([files/1, strings/1]). + +%% Exported for unit testing in other projects +-export([merger/1, string_fun_factory/0]). --endif. -type schema() :: { [cuttlefish_translation:translation()], @@ -62,7 +63,6 @@ {errorlist, Errors} -> %% These have already been logged. We're not moving forward with this %% but, return them anyway so the rebar plugin can display them - %% with io:format, since it doesn't have lager. {errorlist, Errors}; {Translations, Mappings, Validators} -> NewMappings = lists:foldr( @@ -120,7 +120,7 @@ -spec file(string(), schema()) -> schema() | cuttlefish_error:errorlist(). file(Filename, Schema) -> - {ok, B} = file:read_file(Filename), + {ok, B, _} = erl_prim_loader:get_file(filename:absname(Filename)), %% latin-1 is easier to support generically. We'll revisit utf-8 %% support in the future. S = unicode:characters_to_list(B, latin1), @@ -158,7 +158,7 @@ {error, {Line, erl_scan, _}, _} -> Error = {erl_scan, Line}, ErrStr = cuttlefish_error:xlate(Error), - lager:error(lists:flatten(ErrStr)), + _ = ?LOG_ERROR(lists:flatten(ErrStr)), {errorlist, [{error, Error}]} end. @@ -323,14 +323,15 @@ ok. bad_file_test() -> - cuttlefish_lager_test_backend:bounce(), - {errorlist, ErrorList} = file("../test/bad_erlang.schema"), + _ = cuttlefish_test_logging:set_up(), + _ = cuttlefish_test_logging:bounce(), + {errorlist, ErrorList} = file("test/bad_erlang.schema"), - Logs = cuttlefish_lager_test_backend:get_logs(), + Logs = cuttlefish_test_logging:get_logs(), [L1|Tail] = Logs, [L2|[]] = Tail, ?assertMatch({match, _}, re:run(L1, "Error scanning erlang near line 10")), - ?assertMatch({match, _}, re:run(L2, "Error parsing schema: ../test/bad_erlang.schema")), + ?assertMatch({match, _}, re:run(L2, "Error parsing schema: test/bad_erlang.schema")), ?assertEqual([ {error, {erl_scan, 10}} @@ -338,7 +339,8 @@ ok. parse_invalid_erlang_test() -> - cuttlefish_lager_test_backend:bounce(), + _ = cuttlefish_test_logging:set_up(), + _ = cuttlefish_test_logging:bounce(), SchemaString = lists:flatten([ "%% @doc some doc\n", "%% the doc continues!\n", @@ -348,7 +350,7 @@ ]), Parsed = string(SchemaString), - [Log] = cuttlefish_lager_test_backend:get_logs(), + [Log] = cuttlefish_test_logging:get_logs(), ?assertMatch({match, _}, re:run(Log, "Schema parse error near line number 4")), ?assertMatch({match, _}, re:run(Log, "syntax error before: ")), ?assertMatch({match, _}, re:run(Log, "'}'")), @@ -358,7 +360,8 @@ parse_bad_datatype_test() -> - cuttlefish_lager_test_backend:bounce(), + _ = cuttlefish_test_logging:set_up(), + _ = cuttlefish_test_logging:bounce(), SchemaString = lists:flatten([ "%% @doc some doc\n", @@ -369,17 +372,16 @@ "]}.\n" ]), _Parsed = string(SchemaString), - ?assertEqual([], cuttlefish_lager_test_backend:get_logs()). + ?assertEqual([], cuttlefish_test_logging:get_logs()). files_test() -> - lager:start(), %% files/1 takes a list of schemas in priority order. %% Loads them in reverse order, as things are overridden {Translations, Mappings, Validators} = files( [ - "../test/multi1.schema", - "../test/multi2.schema", - "../test/multi3.schema" + "test/multi1.schema", + "test/multi2.schema", + "test/multi3.schema" ]), ?assertEqual(6, length(Mappings)), diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_translation.erl erlang-cuttlefish-3.0.1/src/cuttlefish_translation.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_translation.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_translation.erl 2021-03-26 15:47:17.000000000 +0000 @@ -23,12 +23,11 @@ -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. -record(translation, { mapping::string(), - func::fun() + func::fun() | undefined }). -type translation() :: #translation{}. -type translation_fun() :: fun(([proplists:property()]) -> any()). diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_unit.erl erlang-cuttlefish-3.0.1/src/cuttlefish_unit.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_unit.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_unit.erl 2021-03-26 15:47:17.000000000 +0000 @@ -1,7 +1,9 @@ -module(cuttlefish_unit). +-include_lib("kernel/include/logger.hrl"). + -include_lib("eunit/include/eunit.hrl"). --compile(export_all). +-compile([nowarn_export_all, export_all]). generate_templated_config(FileName, Conf, Context) -> generate_templated_config(FileName, Conf, Context, {[], [], []}). @@ -17,7 +19,7 @@ cuttlefish_generator:map(Schema, Conf). render_template(FileName, Context) -> - {ok, Bin} = file:read_file(FileName), + {ok, Bin, _} = erl_prim_loader:get_file(filename:absname(FileName)), %% Stolen from rebar_templater:render/2 %% Be sure to escape any double-quotes before rendering... ReOpts = [global, {return, list}], @@ -25,11 +27,20 @@ Str1 = re:replace(Str0, "\"", "\\\\\"", ReOpts), %% the mustache module is only available in the context of a rebar run. - case {code:ensure_loaded(mustache), code:ensure_loaded(rebar_mustache)} of - {{module, mustache}, _} -> + case {code:ensure_loaded(mustache), code:ensure_loaded(rebar_mustache), code:ensure_loaded(bbmustache)} of + {{module, mustache}, _, _} -> mustache:render(Str1, dict:from_list(Context)); - {_, {module, rebar_mustache}} -> + {_, {module, rebar_mustache}, _} -> rebar_mustache:render(Str1, dict:from_list(Context)); + {_, _, {module, bbmustache}} -> + Ret = bbmustache:render( + Bin, + maps:from_list( + [case is_atom(K) of + true -> {atom_to_list(K), V}; + false -> I + end || I = {K, V} <- Context])), + binary_to_list(Ret); _ -> io:format("mustache and/or rebar_mustache module not loaded. " "This test can only be run in a rebar context.~n") @@ -95,28 +106,6 @@ assert_error(Config) -> ?assertMatch({error, _, {errorlist, _}}, Config). -%% @doc Asserts that the generated configuration is in error, with the -%% error occurring in a specific phase. -assert_error_in_phase(Config, Phase) when is_atom(Phase) -> - ?assertMatch({error, Phase, {errorlist, _}}, Config). - -%% @doc Asserts that the generated configuration is in error, and the -%% given error message was emitted by the given phase. -assert_error(Config, Phase, Message) -> - assert_error_in_phase(Config, Phase), - assert_error_message(Config, Message). - -%% @doc Asserts that the generated configuration is in error and has -%% the given error messages. -assert_errors(Config, [H|_]=Messages) when is_list(H) -> - [ assert_error_message(Config, Message) || Message <- Messages ]. - -%% @doc Asserts that the generated configuration is in error, with -%% errors occuring in the given phase and containing the given -%% messages. -assert_errors(Config, Phase, [H|_]=Messages) when is_list(H) -> - assert_error_in_phase(Config, Phase), - [ assert_error_message(Config, Message) || Message <- Messages ]. %% @doc Asserts that the generated configuration is in error and %% contains an error tuple that translates to the given error message @@ -181,7 +170,6 @@ ok. multiple_schema_generate_templated_config_test() -> - lager:start(), Context = [ {mustache, "mustache"} ], @@ -192,8 +180,8 @@ ]}) ], []}, - Config = cuttlefish_unit:generate_templated_config("../test/sample_mustache.schema", [], Context, PrereqSchema), - lager:error("~p", [Config]), + Config = cuttlefish_unit:generate_templated_config("test/sample_mustache.schema", [], Context, PrereqSchema), + _ = ?LOG_ERROR("~p", [Config]), assert_config(Config, "app_a.setting_b", "/c/mustache/a.b"), ok. diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_util.erl erlang-cuttlefish-3.0.1/src/cuttlefish_util.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_util.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_util.erl 2021-03-26 15:47:17.000000000 +0000 @@ -21,9 +21,10 @@ %% ------------------------------------------------------------------- -module(cuttlefish_util). +-include_lib("kernel/include/logger.hrl"). + -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. -export([ @@ -44,27 +45,27 @@ %% @deprecated conf_get_value(Key, Conf) -> - lager:warning("cuttlefish_util:conf_get_value/2 has been deprecated. use cuttlefish:conf_get/2"), + _ = ?LOG_WARNING("cuttlefish_util:conf_get_value/2 has been deprecated. use cuttlefish:conf_get/2"), cuttlefish:conf_get(Key, Conf). %% @deprecated conf_get_value(Key, Conf, Default) -> - lager:warning("cuttlefish_util:conf_get_value/3 has been deprecated. use cuttlefish:conf_get/3"), + _ = ?LOG_WARNING("cuttlefish_util:conf_get_value/3 has been deprecated. use cuttlefish:conf_get/3"), cuttlefish:conf_get(Key, Conf, Default). %% @deprecated filter_by_variable_starts_with(Prefix, Conf) -> - lager:warning("cuttlefish_util:filter_by_variable_starts_with/2 has been deprecated. use cuttlefish_variable:filter_by_prefix/2"), + _ = ?LOG_WARNING("cuttlefish_util:filter_by_variable_starts_with/2 has been deprecated. use cuttlefish_variable:filter_by_prefix/2"), cuttlefish_variable:filter_by_prefix(Prefix, Conf). %% @deprecated matches_for_variable_def(VarDef, Conf) -> - lager:warning("cuttlefish_util:matches_for_variable_def/2 has been deprecated. use cuttlefish_variable:fuzzy_matches/2"), + _ = ?LOG_WARNING("cuttlefish_util:matches_for_variable_def/2 has been deprecated. use cuttlefish_variable:fuzzy_matches/2"), cuttlefish_variable:fuzzy_matches(VarDef, Conf). %% @deprecated fuzzy_variable_match(Var, VarDef) -> - lager:warning("cuttlefish_util:fuzzy_variable_match/2 has been deprecated. use cuttlefish_variable:is_fuzzy_match/2"), + _ = ?LOG_WARNING("cuttlefish_util:fuzzy_variable_match/2 has been deprecated. use cuttlefish_variable:is_fuzzy_match/2"), cuttlefish_variable:is_fuzzy_match(Var, VarDef). %% @doc replace the element in a proplist @@ -171,16 +172,6 @@ ?assertEqual(4, levenshtein("four", "")), ok. -print_error_test() -> - application:stop(lager), - case lager:error("Error") of - {error, lager_not_running} -> - ?assertEqual(ok, (cuttlefish_error:print("Error"))); - Other -> - ?assertEqual({error, lager_not_running}, Other) - end, - ok. - ceiling_test() -> ?assertEqual(9, ceiling(8.99999)), ?assertEqual(9, ceiling(8.00001)), @@ -189,4 +180,10 @@ ?assertEqual(-2, ceiling(-2.9999999)), ok. +numerify_test() -> + ?assertEqual(42, numerify("42")), + ?assertEqual(42.0, numerify("42.0")), + ?assertEqual(0.5, numerify(".5")), + ok. + -endif. diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_validator.erl erlang-cuttlefish-3.0.1/src/cuttlefish_validator.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_validator.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_validator.erl 2021-03-26 15:47:17.000000000 +0000 @@ -23,7 +23,6 @@ -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. -record(validator, { diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_variable.erl erlang-cuttlefish-3.0.1/src/cuttlefish_variable.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_variable.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_variable.erl 2021-03-26 15:47:17.000000000 +0000 @@ -30,7 +30,6 @@ -define(QC_OUT(Prop), on_output(fun(F,A) -> io:format(user, F, A) end, Prop)). -endif. -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. -export([ diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_vmargs.erl erlang-cuttlefish-3.0.1/src/cuttlefish_vmargs.erl --- erlang-cuttlefish-2.0.11+dfsg/src/cuttlefish_vmargs.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/cuttlefish_vmargs.erl 2021-03-26 15:47:17.000000000 +0000 @@ -4,7 +4,6 @@ -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -endif. %% @doc turns a proplist into a list of strings suitable for vm.args files diff -Nru erlang-cuttlefish-2.0.11+dfsg/src/lager_stderr_backend.erl erlang-cuttlefish-3.0.1/src/lager_stderr_backend.erl --- erlang-cuttlefish-2.0.11+dfsg/src/lager_stderr_backend.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/src/lager_stderr_backend.erl 1970-01-01 00:00:00.000000000 +0000 @@ -1,351 +0,0 @@ -%% Copyright (c) 2011-2012 Basho Technologies, Inc. All Rights Reserved. -%% -%% This file is provided 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. - -%% @doc Console backend for lager. Configured with a single option, the loglevel -%% desired. - --module(lager_stderr_backend). - --behaviour(gen_event). - --export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, - code_change/3]). - --record(state, {level :: {'mask', integer()}, - formatter :: atom(), - format_config :: any(), - colors=[] :: list()}). - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --compile([{parse_transform, lager_transform}]). --endif. - --include_lib("lager/include/lager.hrl"). --define(TERSE_FORMAT,[time, " ", color, "[", severity,"] ", message]). - -%% @private -init([Level, true]) -> % for backwards compatibility - init([Level,{lager_default_formatter,[{eol, eol()}]}]); -init([Level,false]) -> % for backwards compatibility - init([Level,{lager_default_formatter,?TERSE_FORMAT ++ [eol()]}]); -init([Level,{Formatter,FormatterConfig}]) when is_atom(Formatter) -> - Colors = case application:get_env(lager, colored) of - {ok, true} -> - {ok, LagerColors} = application:get_env(lager, colors), - LagerColors; - _ -> [] - end, - - try {is_new_style_console_available(), lager_util:config_to_mask(Level)} of - {false, _} -> - Msg = "Lager's console backend is incompatible with the 'old' shell, not enabling it", - %% be as noisy as possible, log to every possible place - try - alarm_handler:set_alarm({?MODULE, "WARNING: " ++ Msg}) - catch - _:_ -> - error_logger:warning_msg(Msg ++ "~n") - end, - io:format("WARNING: " ++ Msg ++ "~n"), - ?INT_LOG(warning, Msg, []), - {error, {fatal, old_shell}}; - {true, Levels} -> - {ok, #state{level=Levels, - formatter=Formatter, - format_config=FormatterConfig, - colors=Colors}} - catch - _:_ -> - {error, {fatal, bad_log_level}} - end; -init(Level) -> - init([Level,{lager_default_formatter,?TERSE_FORMAT ++ [eol()]}]). - - -%% @private -handle_call(get_loglevel, #state{level=Level} = State) -> - {ok, Level, State}; -handle_call({set_loglevel, Level}, State) -> - try lager_util:config_to_mask(Level) of - Levels -> - {ok, ok, State#state{level=Levels}} - catch - _:_ -> - {ok, {error, bad_log_level}, State} - end; -handle_call(_Request, State) -> - {ok, ok, State}. - -%% @private -handle_event({log, Message}, - #state{level=L,formatter=Formatter,format_config=FormatConfig,colors=Colors} = State) -> - case lager_util:is_loggable(Message, L, ?MODULE) of - true -> - io:put_chars(standard_error, Formatter:format(Message,FormatConfig,Colors)), - {ok, State}; - false -> - {ok, State} - end; -handle_event(_Event, State) -> - {ok, State}. - -%% @private -handle_info(_Info, State) -> - {ok, State}. - -%% @private -terminate(_Reason, _State) -> - ok. - -%% @private -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -eol() -> - case application:get_env(lager, colored) of - {ok, true} -> - "\e[0m\r\n"; - _ -> - "\r\n" - end. - --ifdef(TEST). -is_new_style_console_available() -> - true. --else. -is_new_style_console_available() -> - %% Criteria: - %% 1. If the user has specified '-noshell' on the command line, - %% then we will pretend that the new-style console is available. - %% If there is no shell at all, then we don't have to worry - %% about log events being blocked by the old-style shell. - %% 2. Windows doesn't support the new shell, so all windows users - %% have is the oldshell. - %% 3. If the user_drv process iss registered, all is OK. - %% 'user_drv' is a registered proc name used by the "new" - %% console driver. - init:get_argument(noshell) /= error orelse - element(1, os:type()) == win32 orelse - is_pid(whereis(user_drv)). --endif. - --ifdef(TEST). -console_log_test_() -> - %% tiny recursive fun that pretends to be a group leader - F = fun(Self) -> - fun() -> - YComb = fun(Fun) -> - receive - {io_request, From, ReplyAs, {put_chars, unicode, _Msg}} = Y -> - From ! {io_reply, ReplyAs, ok}, - Self ! Y, - Fun(Fun); - Other -> - ?debugFmt("unexpected message ~p~n", [Other]), - Self ! Other - end - end, - YComb(YComb) - end - end, - {foreach, - fun() -> - error_logger:tty(false), - application:load(lager), - application:set_env(lager, handlers, []), - application:set_env(lager, error_logger_redirect, false), - lager:start(), - whereis(standard_error) - end, - fun(User) -> - unregister(standard_error), - register(standard_error, User), - application:stop(lager), - error_logger:tty(true) - end, - [ - {"regular console logging", - fun() -> - Pid = spawn(F(self())), - unregister(standard_error), - register(standard_error, Pid), - erlang:group_leader(Pid, whereis(lager_event)), - gen_event:add_handler(lager_event, lager_stderr_backend, info), - lager_config:set(loglevel, {element(2, lager_util:config_to_mask(info)), []}), - lager:log(info, self(), "Test message"), - receive - {io_request, From, ReplyAs, {put_chars, unicode, Msg}} -> - From ! {io_reply, ReplyAs, ok}, - TestMsg = "Test message" ++ eol(), - ?assertMatch([_, "[info]", TestMsg], re:split(Msg, " ", [{return, list}, {parts, 3}])) - after - 500 -> - ?assert(false) - end - end - }, - {"verbose console logging", - fun() -> - Pid = spawn(F(self())), - unregister(standard_error), - register(standard_error, Pid), - erlang:group_leader(Pid, whereis(lager_event)), - gen_event:add_handler(lager_event, lager_stderr_backend, [info, true]), - lager_config:set(loglevel, {element(2, lager_util:config_to_mask(info)), []}), - lager:info("Test message"), - PidStr = pid_to_list(self()), - receive - {io_request, _, _, {put_chars, unicode, Msg}} -> - TestMsg = "Test message" ++ eol(), - ?assertMatch([_, _, "[info]", PidStr, _, TestMsg], re:split(Msg, "[ @]", [{return, list}, {parts, 6}])) - after - 500 -> - ?assert(false) - end - end - }, - {"custom format console logging", - fun() -> - Pid = spawn(F(self())), - unregister(standard_error), - register(standard_error, Pid), - erlang:group_leader(Pid, whereis(lager_event)), - gen_event:add_handler(lager_event, lager_stderr_backend, - [info, {lager_default_formatter, [date,"#",time,"#",severity,"#",node,"#",pid,"#", - module,"#",function,"#",file,"#",line,"#",message,"\r\n"]}]), - lager_config:set(loglevel, {?INFO, []}), - lager:info("Test message"), - PidStr = pid_to_list(self()), - NodeStr = atom_to_list(node()), - ModuleStr = atom_to_list(?MODULE), - receive - {io_request, _, _, {put_chars, unicode, Msg}} -> - TestMsg = "Test message" ++ eol(), - ?assertMatch([_, _, "info", NodeStr, PidStr, ModuleStr, _, _, _, TestMsg], - re:split(Msg, "#", [{return, list}, {parts, 10}])) - after - 500 -> - ?assert(false) - end - end - }, - {"blacklisting a loglevel works", - fun() -> - Pid = spawn(F(self())), - unregister(standard_error), - register(standard_error, Pid), - gen_event:add_handler(lager_event, lager_stderr_backend, info), - lager_config:set(loglevel, {element(2, lager_util:config_to_mask(info)), []}), - lager:set_loglevel(lager_stderr_backend, '!=info'), - erlang:group_leader(Pid, whereis(lager_event)), - lager:debug("Test message"), - receive - {io_request, From1, ReplyAs1, {put_chars, unicode, Msg1}} -> - From1 ! {io_reply, ReplyAs1, ok}, - TestMsg = "Test message" ++ eol(), - ?assertMatch([_, "[debug]", TestMsg], re:split(Msg1, " ", [{return, list}, {parts, 3}])) - after - 1000 -> - ?assert(false) - end, - %% info is blacklisted - lager:info("Test message"), - receive - {io_request, From2, ReplyAs2, {put_chars, unicode, _Msg2}} -> - From2 ! {io_reply, ReplyAs2, ok}, - %% OOPS ?assert(false) - ok - after - 500 -> - ?assert(true) - end - end - }, - {"whitelisting a loglevel works", - fun() -> - Pid = spawn(F(self())), - unregister(standard_error), - register(standard_error, Pid), - gen_event:add_handler(lager_event, lager_stderr_backend, info), - lager_config:set(loglevel, {element(2, lager_util:config_to_mask(info)), []}), - lager:set_loglevel(lager_stderr_backend, '=debug'), - erlang:group_leader(Pid, whereis(lager_event)), - lager:debug("Test message"), - receive - {io_request, From1, ReplyAs1, {put_chars, unicode, Msg1}} -> - From1 ! {io_reply, ReplyAs1, ok}, - TestMsg = "Test message" ++ eol(), - ?assertMatch([_, "[debug]", TestMsg], re:split(Msg1, " ", [{return, list}, {parts, 3}])) - after - 1000 -> - ?assert(false) - end, - %% info is blacklisted - lager:error("Test message"), - receive - {io_request, From2, ReplyAs2, {put_chars, unicode, _Msg2}} -> - From2 ! {io_reply, ReplyAs2, ok}, - %% OOPS! ?assert(false) - ok - after - 500 -> - ?assert(true) - end - end - } - ] - }. - -set_loglevel_test_() -> - {foreach, - fun() -> - error_logger:tty(false), - application:load(lager), - application:set_env(lager, handlers, [{lager_stderr_backend, info}]), - application:set_env(lager, error_logger_redirect, false), - application:start(lager) - end, - fun(_) -> - application:stop(lager), - error_logger:tty(true) - end, - [ - {"Get/set loglevel test", - fun() -> - ?assertEqual(info, lager:get_loglevel(lager_stderr_backend)), - lager:set_loglevel(lager_stderr_backend, debug), - ?assertEqual(debug, lager:get_loglevel(lager_stderr_backend)), - lager:set_loglevel(lager_stderr_backend, '!=debug'), - ?assertEqual(info, lager:get_loglevel(lager_stderr_backend)), - lager:set_loglevel(lager_stderr_backend, '!=info'), - ?assertEqual(debug, lager:get_loglevel(lager_stderr_backend)), - ok - end - }, - {"Get/set invalid loglevel test", - fun() -> - ?assertEqual(info, lager:get_loglevel(lager_stderr_backend)), - ?assertEqual({error, bad_log_level}, - lager:set_loglevel(lager_stderr_backend, fatfinger)), - ?assertEqual(info, lager:get_loglevel(lager_stderr_backend)) - end - } - - ] - }. - --endif. diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/conf.d/dir.d/riak.conf erlang-cuttlefish-3.0.1/test/conf.d/dir.d/riak.conf --- erlang-cuttlefish-2.0.11+dfsg/test/conf.d/dir.d/riak.conf 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/conf.d/dir.d/riak.conf 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1,2 @@ +log.console.file = /var/log/console.log +rogue.option = 42 diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/conf.d/riak2.conf erlang-cuttlefish-3.0.1/test/conf.d/riak2.conf --- erlang-cuttlefish-2.0.11+dfsg/test/conf.d/riak2.conf 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/conf.d/riak2.conf 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1,3 @@ +log.syslog = off + +include dir.d/riak.conf diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/conf.d/riak.conf erlang-cuttlefish-3.0.1/test/conf.d/riak.conf --- erlang-cuttlefish-2.0.11+dfsg/test/conf.d/riak.conf 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/conf.d/riak.conf 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1,5 @@ +ring_size = 5 # we want a smaller ring size +anti_entropy = debug +log.error.file = /var/log/error.log +listener.http.internal = 127.0.0.1:8098 +listener.http.external = 10.0.0.1:80 diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/cuttlefish_escript_integration_tests.erl erlang-cuttlefish-3.0.1/test/cuttlefish_escript_integration_tests.erl --- erlang-cuttlefish-2.0.11+dfsg/test/cuttlefish_escript_integration_tests.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/cuttlefish_escript_integration_tests.erl 2021-03-26 15:47:17.000000000 +0000 @@ -1,32 +1,34 @@ -module(cuttlefish_escript_integration_tests). +-include_lib("kernel/include/logger.hrl"). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). escript_utf8_test() -> - cuttlefish_lager_test_backend:bounce(error), + _ = cuttlefish_test_logging:set_up(), + _ = cuttlefish_test_logging:bounce(error), ?assertThrow(stop_deactivate, cuttlefish_escript:main( - "-d ../test_fixtures/escript_utf8_test/generated.config " - "-s ../test_fixtures/escript_utf8_test/lib " - "-e ../test_fixtures/escript_utf8_test/etc " - "-c ../test_fixtures/escript_utf8_test/etc/utf8.conf generate" + "-d test_fixtures/escript_utf8_test/generated.config " + "-s test_fixtures/escript_utf8_test/lib " + "-e test_fixtures/escript_utf8_test/etc " + "-c test_fixtures/escript_utf8_test/etc/utf8.conf generate" )), - [Log] = cuttlefish_lager_test_backend:get_logs(), + [Log] = cuttlefish_test_logging:get_logs(), ?assertMatch({match, _}, re:run(Log, "utf8.conf: Error converting value on line #1 to latin1")), ok. advanced_config_format_test() -> - cuttlefish_lager_test_backend:bounce(error), + _ = cuttlefish_test_logging:set_up(), + _ = cuttlefish_test_logging:bounce(error), ?assertThrow(stop_deactivate, cuttlefish_escript:main( - "-d ../test_fixtures/acformat/generated.config " - "-s ../test_fixtures/acformat/lib " - "-e ../test_fixtures/acformat/etc " - "-c ../test_fixtures/acformat/etc/acformat.conf generate" + "-d test_fixtures/acformat/generated.config " + "-s test_fixtures/acformat/lib " + "-e test_fixtures/acformat/etc " + "-c test_fixtures/acformat/etc/acformat.conf generate" )), - [Log] = cuttlefish_lager_test_backend:get_logs(), - ?assertMatch({match, _}, re:run(Log, "Error parsing [.][.]/test_fixtures/acformat/etc/advanced.config, incorrect format: \\[\\[a\\],\\[b\\]\\]")), + [Log] = cuttlefish_test_logging:get_logs(), + ?assertMatch({match, _}, re:run(Log, "Error parsing test_fixtures/acformat/etc/advanced.config, incorrect format: \\[\\[a\\],\\[b\\]\\]")), ok. escript_prune_test_() -> @@ -38,9 +40,9 @@ escript_prune(DashM, ExpectedMax) -> %% Empty workspace - case file:list_dir("../test_fixtures/escript_prune_test/generated.config") of + case file:list_dir("test_fixtures/escript_prune_test/generated.config") of {ok, FilenamesToDelete} -> - [ file:delete(filename:join(["../test_fixtures/escript_prune_test/generated.config",F])) || F <- FilenamesToDelete ]; + [ file:delete(filename:join(["test_fixtures/escript_prune_test/generated.config",F])) || F <- FilenamesToDelete ]; _ -> ok end, @@ -50,20 +52,20 @@ %% Timer to keep from generating more than one file per second timer:sleep(1100), cuttlefish_escript:main( - "-d ../test_fixtures/escript_prune_test/generated.config " - "-s ../test_fixtures/escript_prune_test/lib " - "-e ../test_fixtures/escript_prune_test/etc " + "-d test_fixtures/escript_prune_test/generated.config " + "-s test_fixtures/escript_prune_test/lib " + "-e test_fixtures/escript_prune_test/etc " ++ DashM ++ " generate" ), AppConfigs = lists:sort( filelib:wildcard("app.*.config", - "../test_fixtures/escript_prune_test/generated.config")), + "test_fixtures/escript_prune_test/generated.config")), VMArgs = lists:sort( filelib:wildcard("vm.*.args", - "../test_fixtures/escript_prune_test/generated.config")), + "test_fixtures/escript_prune_test/generated.config")), {AppConfigs, VMArgs, diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/cuttlefish_escript_test.erl erlang-cuttlefish-3.0.1/test/cuttlefish_escript_test.erl --- erlang-cuttlefish-2.0.11+dfsg/test/cuttlefish_escript_test.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/cuttlefish_escript_test.erl 2021-03-26 15:47:17.000000000 +0000 @@ -1,17 +1,28 @@ -module(cuttlefish_escript_test). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -define(assertPrinted(___Text), + ?assertPrinted(___Text, [])). + +-define(assertPrinted(___Text, ___Opts), begin ((fun() -> case cuttlefish_test_group_leader:get_output() of {ok, ___Output} -> - case re:run(___Output, ___Text) of - {match, _} -> + case {lists:member(exact, ___Opts), + re:run(___Output, ___Text)} of + {true, _} when ___Output =:= ___Text -> + ok; + {true, _} when ___Output =/= ___Text -> + erlang:error({assertPrinted_failed, + [{module, ?MODULE}, + {line, ?LINE}, + {expected, ___Text}, + {actual, unicode:characters_to_list(___Output)}]}); + {_, {match, _}} -> ok; - nomatch -> + {_, nomatch} -> erlang:error({assertPrinted_failed, [{module, ?MODULE}, {line, ?LINE}, @@ -51,7 +62,7 @@ ]. describe(Key) -> - ?assertThrow(stop_deactivate, cuttlefish_escript:main(["-i", "../test/riak.schema", "-c", "../test/riak.conf", "describe", Key])). + ?assertThrow(stop_deactivate, cuttlefish_escript:main(["-i", "test/riak.schema", "-c", "test/riak.conf", "describe", Key])). describe_prints_docs() -> ?capturing(begin @@ -94,7 +105,19 @@ describe_prints_not_configured() -> ?capturing(begin describe("ssl.keyfile"), - ?assertPrinted("Value not set in \\.\\./test/riak.conf") + ?assertPrinted("Value not set in test/riak.conf") + end). + +silent_test() -> + ?capturing(begin + cuttlefish_escript:main(["-i", "test/riak.schema", "-c", "test/riak.conf", "--etc_dir", "etc", "--silent"]), + ?assertPrinted("", [exact]) + end). + +vm_args_test() -> + ?capturing(begin + cuttlefish_escript:main(["--schema_file", "priv/erlang_vm.schema", "--conf_file", "test/riak.conf", "--etc_dir", "etc", "--dest_file", "vm.generated.args", "--allow_extra", "--silent"]), + ?assertPrinted("", [exact]) end). -endif. diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/cuttlefish_integration_test.erl erlang-cuttlefish-3.0.1/test/cuttlefish_integration_test.erl --- erlang-cuttlefish-2.0.11+dfsg/test/cuttlefish_integration_test.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/cuttlefish_integration_test.erl 2021-03-26 15:47:17.000000000 +0000 @@ -1,40 +1,58 @@ -module(cuttlefish_integration_test). +-include_lib("kernel/include/logger.hrl"). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). -%% This test generates a default .conf file from the riak.schema. view it at ../generated.conf +%% This test generates a default .conf file from the riak.schema. view it at generated.conf generated_conf_file_test() -> - {_, Mappings, _} = cuttlefish_schema:file("../test/riak.schema"), - cuttlefish_conf:generate_file(Mappings, "../generated.conf"), + {_, Mappings, _} = cuttlefish_schema:file("test/riak.schema"), + cuttlefish_conf:generate_file(Mappings, "generated.conf"), %% Schema generated a conf file, let's parse it! - Conf = cuttlefish_conf:file("../generated.conf"), + Conf = cuttlefish_conf:file("generated.conf"), ?assertEqual("8099", proplists:get_value(["handoff","port"], Conf)), ok. -%% This test generates a .config file from the riak.schema. view it at ../generated.config +%% Same as above, but with the files in an .ez archive. +generated_conf_file_ez_test() -> + {_, Mappings, _} = cuttlefish_schema:file("test/riakconf.ez/riakconf/riak.schema"), + cuttlefish_conf:generate_file(Mappings, "generated.conf"), + %% Schema generated a conf file, let's parse it! + Conf = cuttlefish_conf:file("generated.conf"), + ?assertEqual("8099", proplists:get_value(["handoff","port"], Conf)), + ok. + +%% This test generates a .config file from the riak.schema. view it at generated.config generated_config_file_test() -> - Schema = cuttlefish_schema:file("../test/riak.schema"), - Conf = [], %% conf_parse:file("../test/riak.conf"), + Schema = cuttlefish_schema:file("test/riak.schema"), + Conf = [], %% conf_parse:file("test/riak.conf"), + NewConfig = cuttlefish_generator:map(Schema, Conf), + + file:write_file("generated.config",io_lib:fwrite("~p.\n",[NewConfig])), + ok. + +%% Same as above, but with the files in an .ez archive. +generated_config_file_ez_test() -> + Schema = cuttlefish_schema:file("test/riakconf.ez/riakconf/riak.schema"), + Conf = [], %% conf_parse:file("test/riak.conf"), NewConfig = cuttlefish_generator:map(Schema, Conf), - file:write_file("../generated.config",io_lib:fwrite("~p.\n",[NewConfig])), + file:write_file("generated.config",io_lib:fwrite("~p.\n",[NewConfig])), ok. breaks_on_fuzzy_and_strict_match_test() -> - Schema = cuttlefish_schema:file("../test/riak.schema"), + Schema = cuttlefish_schema:file("test/riak.schema"), Conf = [{["listener", "protobuf", "$name"], "127.0.0.1:8087"}], ?assertMatch({error, add_defaults, _}, cuttlefish_generator:map(Schema, Conf)), ok. breaks_on_rhs_not_found_test() -> - Schema = cuttlefish_schema:file("../test/riak.schema"), + Schema = cuttlefish_schema:file("test/riak.schema"), Conf = [{["ring", "state_dir"], "$(tyktorp)/ring"}], ?assertMatch({error, rhs_subs, _}, cuttlefish_generator:map(Schema, Conf)), ok. breaks_on_rhs_infinite_loop_test() -> - Schema = cuttlefish_schema:file("../test/riak.schema"), + Schema = cuttlefish_schema:file("test/riak.schema"), Conf = [ {["ring", "state_dir"], "$(platform_data_dir)/ring"}, {["platform_data_dir"], "$(ring.state_dir)/data"} @@ -43,27 +61,27 @@ ok. breaks_on_bad_enum_test() -> - Schema = cuttlefish_schema:file("../test/riak.schema"), + Schema = cuttlefish_schema:file("test/riak.schema"), Conf = [{["storage_backend"], penguin}], ?assertMatch({error, transform_datatypes, _}, cuttlefish_generator:map(Schema, Conf)), ok. breaks_on_bad_validation_test() -> - Schema = cuttlefish_schema:file("../test/riak.schema"), + Schema = cuttlefish_schema:file("test/riak.schema"), Conf = [{["ring_size"], 10}], ?assertMatch({error, validation, _}, cuttlefish_generator:map(Schema, Conf)), ok. %% Tests that the schema can generate a default app.config from nothing all_the_marbles_test() -> - Schema = cuttlefish_schema:file("../test/riak.schema"), - Conf = [], %conf_parse:file("../test/riak.conf"), + Schema = cuttlefish_schema:file("test/riak.schema"), + Conf = [], %conf_parse:file("test/riak.conf"), NewConfig = cuttlefish_generator:map(Schema, Conf), ?assert(is_proplist(NewConfig)), NewConfigWithoutVmargs = proplists:delete(vm_args, NewConfig), - {ok, [AppConfig]} = file:consult("../test/default.config"), + {ok, [AppConfig]} = file:consult("test/default.config"), ?assert(is_proplist(AppConfig)), @@ -71,8 +89,7 @@ ok. multibackend_test() -> - lager:start(), - Schema = cuttlefish_schema:files(["../test/riak.schema", "../test/multi_backend.schema"]), + Schema = cuttlefish_schema:files(["test/riak.schema", "test/multi_backend.schema"]), Conf = [ {["storage_backend"], "multi"}, @@ -91,7 +108,7 @@ Multi = proplists:get_value(multi_backend, KV), {<<"bitcask_mult">>, riak_kv_bitcask_backend, BitcaskProps} = lists:keyfind(<<"bitcask_mult">>, 1, Multi), - lager:info("BitcaskProps: ~p", [BitcaskProps]), + _ = ?LOG_INFO("BitcaskProps: ~p", [BitcaskProps]), ?assertEqual("/path/to/dat/cask", proplists:get_value(data_root, BitcaskProps)), ?assertEqual(4, proplists:get_value(open_timeout, BitcaskProps)), ?assertEqual(2147483648, proplists:get_value(max_file_size, BitcaskProps)), @@ -142,26 +159,23 @@ ok. unset_translation_test() -> - lager:start(), - Schema = cuttlefish_schema:files(["../test/unset_translation.schema"]), + Schema = cuttlefish_schema:files(["test/unset_translation.schema"]), Conf = [ {["a", "b"], "8"} ], NewConfig = cuttlefish_generator:map(Schema, Conf), Props = proplists:get_value(erlang, NewConfig), - lager:info("~p", [NewConfig]), + _ = ?LOG_INFO("~p", [NewConfig]), ?assertEqual(8, proplists:get_value(key, Props)). not_found_error_test() -> - lager:start(), - Schema = cuttlefish_schema:files(["../test/throw_not_found.schema"]), + Schema = cuttlefish_schema:files(["test/throw_not_found.schema"]), Conf = [], NewConfig = cuttlefish_generator:map(Schema, Conf), ?assertMatch({error, apply_translations, _}, NewConfig). duration_test() -> - lager:start(), - Schema = cuttlefish_schema:files(["../test/durations.schema"]), + Schema = cuttlefish_schema:files(["test/durations.schema"]), %% Test that the duration parsing doesn't emit "error" into the %% config instead of the extended type. diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/cuttlefish_lager_test_backend.erl erlang-cuttlefish-3.0.1/test/cuttlefish_lager_test_backend.erl --- erlang-cuttlefish-2.0.11+dfsg/test/cuttlefish_lager_test_backend.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/cuttlefish_lager_test_backend.erl 1970-01-01 00:00:00.000000000 +0000 @@ -1,130 +0,0 @@ --module(cuttlefish_lager_test_backend). - --behavior(gen_event). - -%% gen_event callbacks --export([init/1, - handle_call/2, - handle_event/2, - handle_info/2, - terminate/2, - code_change/3]). --export([get_logs/0, - bounce/0, - bounce/1]). - -%% holds the log messages for retreival on terminate --record(state, {level :: {mask, integer()}, - verbose :: boolean(), - log = [] :: list()}). - --include_lib("lager/include/lager.hrl"). - --spec get_logs() -> [iolist()] | {error, term()}. -get_logs() -> - gen_event:call(lager_event, ?MODULE, get_logs, infinity). - -bounce() -> - bounce(error). - -bounce(Level) -> - application:stop(lager), - lager:start(), - gen_event:add_handler(lager_event, cuttlefish_lager_test_backend, [error, false]), - lager:set_loglevel(cuttlefish_lager_test_backend, Level), - ok. - --spec(init(integer()|atom()|[term()]) -> {ok, #state{}} | {error, atom()}). -%% @private -%% @doc Initializes the event handler -init(Level) when is_atom(Level) -> - case lists:member(Level, ?LEVELS) of - true -> - {ok, #state{level=lager_util:level_to_num(Level), verbose=false}}; - _ -> - {error, bad_log_level} - end; -init([Level, Verbose]) -> - case lists:member(Level, ?LEVELS) of - true -> - {ok, #state{level=lager_util:level_to_num(Level), verbose=Verbose}}; - _ -> - {error, bad_log_level} - end. - --spec(handle_event(tuple(), #state{}) -> {ok, #state{}}). -%% @private -%% @doc handles the event, adding the log message to the gen_event's state. -%% this function attempts to handle logging events in both the simple tuple -%% and new record (introduced after lager 1.2.1) formats. -handle_event({log, Dest, Level, {Date, Time}, [LevelStr, Location, Message]}, %% lager 1.2.1 - #state{level=L, verbose=Verbose, log = Logs} = State) when Level > L -> - case lists:member(riak_test_lager_backend, Dest) of - true -> - Log = case Verbose of - true -> - [Date, " ", Time, " ", LevelStr, Location, Message]; - _ -> - [Time, " ", LevelStr, Message] - end, - {ok, State#state{log=[Log|Logs]}}; - false -> - {ok, State} - end; -handle_event({log, Level, {Date, Time}, [LevelStr, Location, Message]}, %% lager 1.2.1 - #state{level=LogLevel, verbose=Verbose, log = Logs} = State) when Level =< LogLevel -> - Log = case Verbose of - true -> - [Date, " ", Time, " ", LevelStr, Location, Message]; - _ -> - [Time, " ", LevelStr, Message] - end, - {ok, State#state{log=[Log|Logs]}}; -handle_event({log, {lager_msg, Dest, _Meta, Level, DateTime, _Timestamp, Message}}, - State) -> %% lager 2.0.0 - case lager_util:level_to_num(Level) of - L when L =< State#state.level -> - handle_event({log, L, DateTime, - [["[",atom_to_list(Level),"] "], " ", Message]}, - State); - L -> - handle_event({log, Dest, L, DateTime, - [["[",atom_to_list(Level),"] "], " ", Message]}, - State) - end; -handle_event(Event, State) -> - {ok, State#state{log = [Event|State#state.log]}}. - --spec(handle_call(any(), #state{}) -> {ok, any(), #state{}}). -%% @private -%% @doc gets and sets loglevel. This is part of the lager backend api. -handle_call(get_loglevel, #state{level=Level} = State) -> - {ok, Level, State}; -handle_call({set_loglevel, Level}, State) -> - case lists:member(Level, ?LEVELS) of - true -> - {ok, ok, State#state{level=lager_util:level_to_num(Level)}}; - _ -> - {ok, {error, bad_log_level}, State} - end; -handle_call(get_logs, #state{log = Logs} = State) -> - {ok, lists:reverse(Logs), State}; -handle_call(_, State) -> - {ok, ok, State}. - --spec(handle_info(any(), #state{}) -> {ok, #state{}}). -%% @private -%% @doc gen_event callback, does nothing. -handle_info(_, State) -> - {ok, State}. - --spec(code_change(any(), #state{}, any()) -> {ok, #state{}}). -%% @private -%% @doc gen_event callback, does nothing. -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - --spec(terminate(any(), #state{}) -> {ok, list()}). -%% @doc gen_event callback, does nothing. -terminate(_Reason, #state{log=Logs}) -> - {ok, lists:reverse(Logs)}. diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/cuttlefish_nested_schema_test.erl erlang-cuttlefish-3.0.1/test/cuttlefish_nested_schema_test.erl --- erlang-cuttlefish-2.0.11+dfsg/test/cuttlefish_nested_schema_test.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/cuttlefish_nested_schema_test.erl 2021-03-26 15:47:17.000000000 +0000 @@ -1,7 +1,6 @@ -module(cuttlefish_nested_schema_test). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). nested_schema_test() -> Conf = [ diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/cuttlefish_test_group_leader.erl erlang-cuttlefish-3.0.1/test/cuttlefish_test_group_leader.erl --- erlang-cuttlefish-2.0.11+dfsg/test/cuttlefish_test_group_leader.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/cuttlefish_test_group_leader.erl 2021-03-26 15:47:17.000000000 +0000 @@ -24,6 +24,8 @@ tidy_up/1, get_output/0]). +-compile(nowarn_deprecated_function). + %% @doc spawns the new group leader new_group_leader(Runner) -> spawn_link(?MODULE, group_leader_loop, [Runner, queue:new()]). @@ -83,7 +85,7 @@ Chars -> {ok, queue:in(Chars, Output)} catch - C:T -> {{error, {C,T,erlang:get_stacktrace()}}, Output} + C:T:Trace -> {{error, {C, T, Trace}}, Output} end; io_request({put_chars, _Enc, Chars}, Output) -> io_request({put_chars, Chars}, Output); diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/cuttlefish_test_logging.erl erlang-cuttlefish-3.0.1/test/cuttlefish_test_logging.erl --- erlang-cuttlefish-2.0.11+dfsg/test/cuttlefish_test_logging.erl 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/cuttlefish_test_logging.erl 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1,103 @@ +-module(cuttlefish_test_logging). + +-behaviour(gen_event). + +-include_lib("kernel/include/logger.hrl"). + +%% API +-export([start_link/0, add_handler/0]). +-export([set_up/0, log/2, reset/0, get_logs/0, bounce/0, bounce/1]). + +%% gen_event callbacks +-export([init/1, handle_event/2, handle_call/2, + handle_info/2, terminate/2, code_change/3]). + +-define(SERVER, ?MODULE). + +-record(state, { + logs :: [string() | binary() +]}). + +%% +%% API +%% + +set_up() -> + Pid = case start_link() of + {ok, Pid0} -> Pid0; + {error, {already_started, Pid1}} -> Pid1 + end, + case lists:member(?MODULE, gen_event:which_handlers(Pid)) of + true -> ok; + false -> + gen_event:add_handler(Pid, ?MODULE, []) + end, + ok. + +-spec get_logs() -> [iolist()] | {error, term()}. +get_logs() -> + gen_event:call(?SERVER, ?MODULE, get_logs, infinity). + +bounce() -> + bounce(error). + +bounce(Level) -> + gen_event:call(?SERVER, ?MODULE, reset), + logger:remove_handler(?MODULE), + logger:add_handler(?SERVER, ?MODULE, #{ + level => Level + }), + ok. + +start_link() -> + gen_event:start_link({local, ?SERVER}). + +log(LogEvent, Config) -> + gen_event:call(?SERVER, ?MODULE, {log, LogEvent, Config}). + +reset() -> + gen_event:call(?SERVER, ?MODULE, reset). + + +add_handler() -> + gen_event:add_handler(?SERVER, ?MODULE, []). + +%% +%% Callbacks +%% + +init([]) -> + {ok, #state{ + + logs = [] + }}. + +handle_event(Event, State = #state{logs = Logs}) -> + {ok, State#state{logs = [Event | Logs]}}; +handle_event(_Event, State) -> + {ok, State}. + +handle_call({log, LogEvent, LogConfig}, State = #state{logs = Logs0}) -> + #{formatter := {FModule, FConfig}} = LogConfig, + %% [Time, " ", LevelStr, Message] + Log = FModule:format(LogEvent, FConfig), + Logs = [Log | Logs0], + {ok, Logs, State#state{logs = Logs}}; +handle_call(get_logs, State = #state{logs = Logs}) -> + {ok, lists:reverse(Logs), State}; +handle_call(reset, State) -> + Reply = ok, + {ok, Reply, State#state{logs = []}}; +handle_call(_Request, State) -> + Reply = ok, + {ok, Reply, State}. + + +handle_info(_Info, State) -> + {ok, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff -Nru "/tmp/tmp9h_8kb31/4Jtf2Bin7G/erlang-cuttlefish-2.0.11+dfsg/test/dir with spaces/value 3" "/tmp/tmp9h_8kb31/Ln0LapYNlv/erlang-cuttlefish-3.0.1/test/dir with spaces/value 3" --- "/tmp/tmp9h_8kb31/4Jtf2Bin7G/erlang-cuttlefish-2.0.11+dfsg/test/dir with spaces/value 3" 1970-01-01 00:00:00.000000000 +0000 +++ "/tmp/tmp9h_8kb31/Ln0LapYNlv/erlang-cuttlefish-3.0.1/test/dir with spaces/value 3" 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1 @@ +12.34 diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/erlang_vm_schema_tests.erl erlang-cuttlefish-3.0.1/test/erlang_vm_schema_tests.erl --- erlang-cuttlefish-2.0.11+dfsg/test/erlang_vm_schema_tests.erl 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/erlang_vm_schema_tests.erl 2021-03-26 15:47:17.000000000 +0000 @@ -1,15 +1,14 @@ -module(erlang_vm_schema_tests). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). %% basic schema test will check to make sure that all defaults from the schema %% make it into the generated app.config basic_schema_test() -> - %% The defaults are defined in ../priv/riak_kv.schema and multi_backend.schema. + %% The defaults are defined in priv/riak_kv.schema and multi_backend.schema. %% they are the files under test. Config = cuttlefish_unit:generate_templated_config( - ["../priv/erlang_vm.schema"], [], context()), + ["priv/erlang_vm.schema"], [], context()), cuttlefish_unit:assert_config(Config, "vm_args.-smp", enable), cuttlefish_unit:assert_config(Config, "vm_args.+W", "w"), @@ -65,7 +64,7 @@ ], Config = cuttlefish_unit:generate_templated_config( - ["../priv/erlang_vm.schema"], Conf, context()), + ["priv/erlang_vm.schema"], Conf, context()), cuttlefish_unit:assert_config(Config, "vm_args.-smp", disable), cuttlefish_unit:assert_config(Config, "vm_args.+W", "i"), @@ -104,25 +103,25 @@ {["erlang", "schedulers", "online"], 1} ], Config1 = cuttlefish_unit:generate_templated_config( - ["../priv/erlang_vm.schema"], Conf1, context()), + ["priv/erlang_vm.schema"], Conf1, context()), cuttlefish_unit:assert_config(Config1, "vm_args.+S", "4:1"), Conf2 = [ {["erlang", "schedulers", "total"], 4} ], Config2 = cuttlefish_unit:generate_templated_config( - ["../priv/erlang_vm.schema"], Conf2, context()), + ["priv/erlang_vm.schema"], Conf2, context()), cuttlefish_unit:assert_config(Config2, "vm_args.+S", "4"), Conf3 = [ {["erlang", "schedulers", "online"], 4} ], Config3 = cuttlefish_unit:generate_templated_config( - ["../priv/erlang_vm.schema"], Conf3, context()), + ["priv/erlang_vm.schema"], Conf3, context()), cuttlefish_unit:assert_config(Config3, "vm_args.+S", ":4"), Config4 = cuttlefish_unit:generate_templated_config( - ["../priv/erlang_vm.schema"], [], context()), + ["priv/erlang_vm.schema"], [], context()), cuttlefish_unit:assert_not_configured(Config4, "vm_args.+S"), @@ -139,23 +138,23 @@ CorrectRaw = 32, Conf0 = [], - Config0 = cuttlefish_unit:generate_templated_config(["../priv/erlang_vm.schema"], Conf0, context()), + Config0 = cuttlefish_unit:generate_templated_config(["priv/erlang_vm.schema"], Conf0, context()), cuttlefish_unit:assert_not_configured(Config0, "vm_args.+a"), Conf1 = [{["erlang", "async_threads", "stack_size"], Correct}], - Config1 = cuttlefish_unit:generate_templated_config(["../priv/erlang_vm.schema"], Conf1, context()), + Config1 = cuttlefish_unit:generate_templated_config(["priv/erlang_vm.schema"], Conf1, context()), cuttlefish_unit:assert_config(Config1, "vm_args.+a", CorrectRaw), Conf2 = [{["erlang", "async_threads", "stack_size"], TooSmall}], - Config2 = cuttlefish_unit:generate_templated_config(["../priv/erlang_vm.schema"], Conf2, context()), + Config2 = cuttlefish_unit:generate_templated_config(["priv/erlang_vm.schema"], Conf2, context()), cuttlefish_unit:assert_error_message(Config2, "erlang.async_threads.stack_size invalid, must be in the range of " ++ MinSize ++ " to " ++ MaxSize), Conf3 = [{["erlang", "async_threads", "stack_size"], TooLarge}], - Config3 = cuttlefish_unit:generate_templated_config(["../priv/erlang_vm.schema"], Conf3, context()), + Config3 = cuttlefish_unit:generate_templated_config(["priv/erlang_vm.schema"], Conf3, context()), cuttlefish_unit:assert_error_message(Config3, "erlang.async_threads.stack_size invalid, must be in the range of " ++ MinSize ++ " to " ++ MaxSize), Conf4 = [{["erlang", "async_threads", "stack_size"], Indivisible}], - Config4 = cuttlefish_unit:generate_templated_config(["../priv/erlang_vm.schema"], Conf4, context()), + Config4 = cuttlefish_unit:generate_templated_config(["priv/erlang_vm.schema"], Conf4, context()), cuttlefish_unit:assert_error_message(Config4, "erlang.async_threads.stack_size invalid, must be divisible by " ++ integer_to_list(WordSize)), ok. @@ -191,12 +190,12 @@ lists:foreach(fun({Input, Expected}) -> Config = cuttlefish_unit:generate_templated_config( - ["../priv/erlang_vm.schema"], [{InputConfigPoint, Input}], context()), + ["priv/erlang_vm.schema"], [{InputConfigPoint, Input}], context()), cuttlefish_unit:assert_config(Config, GeneratedConfig, Expected) end, Pass), lists:foreach(fun(Input) -> Config = cuttlefish_unit:generate_templated_config( - ["../priv/erlang_vm.schema"], [{InputConfigPoint, Input}], context()), + ["priv/erlang_vm.schema"], [{InputConfigPoint, Input}], context()), cuttlefish_unit:assert_error_message(Config, InputConfig ++ " invalid, must be a valid IPv4 or IPv6 address") end, Fail). diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/include_dir.conf erlang-cuttlefish-3.0.1/test/include_dir.conf --- erlang-cuttlefish-2.0.11+dfsg/test/include_dir.conf 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/include_dir.conf 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1 @@ +include conf.d/*.conf diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/included_value.conf erlang-cuttlefish-3.0.1/test/included_value.conf --- erlang-cuttlefish-2.0.11+dfsg/test/included_value.conf 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/included_value.conf 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1,6 @@ +value1 = $( false end}. -%% But we can have our own mappings too! And they will come cuttlefish_lager_test_backend +%% But we can have our own mappings too! And they will come cuttlefish_test_logging {mapping, "top_level.var1", "app_a.big_var", []}. {translation, "app_a.big_var", fun(X) -> "tippedy top" end}. {validator, "top.val", "you only validate once.", fun(_AlwaysFalse) -> false end}. Binary files /tmp/tmp9h_8kb31/4Jtf2Bin7G/erlang-cuttlefish-2.0.11+dfsg/test/riakconf.ez and /tmp/tmp9h_8kb31/Ln0LapYNlv/erlang-cuttlefish-3.0.1/test/riakconf.ez differ diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/value1 erlang-cuttlefish-3.0.1/test/value1 --- erlang-cuttlefish-2.0.11+dfsg/test/value1 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/value1 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1 @@ +42 diff -Nru erlang-cuttlefish-2.0.11+dfsg/test/value2 erlang-cuttlefish-3.0.1/test/value2 --- erlang-cuttlefish-2.0.11+dfsg/test/value2 1970-01-01 00:00:00.000000000 +0000 +++ erlang-cuttlefish-3.0.1/test/value2 2021-03-26 15:47:17.000000000 +0000 @@ -0,0 +1,3 @@ +multi +line +value diff -Nru erlang-cuttlefish-2.0.11+dfsg/tools.mk erlang-cuttlefish-3.0.1/tools.mk --- erlang-cuttlefish-2.0.11+dfsg/tools.mk 2017-05-31 08:07:39.000000000 +0000 +++ erlang-cuttlefish-3.0.1/tools.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -test: compile - ./rebar eunit skip_deps=true - -docs: - ./rebar doc skip_deps=true - -xref: compile - ./rebar xref skip_deps=true - -PLT ?= $(HOME)/.riak_combo_dialyzer_plt -LOCAL_PLT = .local_dialyzer_plt -DIALYZER_FLAGS ?= -Wunmatched_returns - -${PLT}: compile -ifneq (,$(wildcard $(PLT))) - dialyzer --check_plt --plt $(PLT) --apps $(DIALYZER_APPS) && \ - dialyzer --add_to_plt --plt $(PLT) --output_plt $(PLT) --apps $(DIALYZER_APPS) ; test $$? -ne 1 -else - dialyzer --build_plt --output_plt $(PLT) --apps $(DIALYZER_APPS); test $$? -ne 1 -endif - -${LOCAL_PLT}: compile -ifneq (,$(wildcard deps/*)) -ifneq (,$(wildcard $(LOCAL_PLT))) - dialyzer --check_plt --plt $(LOCAL_PLT) deps/*/ebin && \ - dialyzer --add_to_plt --plt $(LOCAL_PLT) --output_plt $(LOCAL_PLT) deps/*/ebin ; test $$? -ne 1 -else - dialyzer --build_plt --output_plt $(LOCAL_PLT) deps/*/ebin ; test $$? -ne 1 -endif -endif - -dialyzer: ${PLT} ${LOCAL_PLT} - @echo "==> $(shell basename $(shell pwd)) (dialyzer)" - @if [ -f $(LOCAL_PLT) ]; then \ - dialyzer $(DIALYZER_FLAGS) --plts $(PLT) $(LOCAL_PLT) -c ebin; \ - else \ - dialyzer $(DIALYZER_FLAGS) --plts $(PLT) -c ebin; \ - fi - -cleanplt: - @echo - @echo "Are you sure? It takes several minutes to re-build." - @echo Deleting $(PLT) and $(LOCAL_PLT) in 5 seconds. - @echo - sleep 5 - rm $(PLT) - rm $(LOCAL_PLT) -