You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by va...@apache.org on 2023/02/24 07:31:28 UTC

[couchdb] branch persistent-term-features created (now 955069fa6)

This is an automated email from the ASF dual-hosted git repository.

vatamane pushed a change to branch persistent-term-features
in repository https://gitbox.apache.org/repos/asf/couchdb.git


      at 955069fa6 Use a persistent term for features

This branch includes the following new commits:

     new 955069fa6 Use a persistent term for features

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[couchdb] 01/01: Use a persistent term for features

Posted by va...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

vatamane pushed a commit to branch persistent-term-features
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 955069fa651fc9b3a82a849584723cfa1df29575
Author: Nick Vatamaniuc <va...@apache.org>
AuthorDate: Fri Feb 24 02:28:54 2023 -0500

    Use a persistent term for features
    
    First use is in switching between fips / non-fips mode for md5 hashing at
    runtime to avoid creating two build variants.
    
    Issue: #4442
---
 src/config/src/config.erl                | 29 +++++++++---------
 src/config/test/config_tests.erl         |  6 ++++
 src/couch/src/couch_hash.erl             | 20 +++++++++---
 src/couch/test/eunit/couch_hash_test.erl | 52 ++++++++++++++++++++++++++++++++
 4 files changed, 88 insertions(+), 19 deletions(-)

diff --git a/src/config/src/config.erl b/src/config/src/config.erl
index 7c38ff58f..658fc67d3 100644
--- a/src/config/src/config.erl
+++ b/src/config/src/config.erl
@@ -31,7 +31,7 @@
 -export([get_float/3, set_float/3, set_float/4]).
 -export([get_boolean/3, set_boolean/3, set_boolean/4]).
 
--export([features/0, enable_feature/1, disable_feature/1]).
+-export([features/0, enable_feature/1, disable_feature/1, is_enabled/1]).
 
 -export([listen_for_changes/2]).
 -export([subscribe_for_changes/1]).
@@ -42,7 +42,7 @@
 
 -export([is_sensitive/2]).
 
--define(FEATURES, "features").
+-define(FEATURES, features).
 
 -define(TIMEOUT, 30000).
 -define(INVALID_SECTION, <<"Invalid configuration section">>).
@@ -212,23 +212,22 @@ delete(Section, Key, Persist, Reason) when is_list(Section), is_list(Key) ->
     ).
 
 features() ->
-    application:get_env(config, enabled_features, []).
+    Map = persistent_term:get({?MODULE, ?FEATURES}, #{}),
+    lists:sort(maps:keys(Map)).
 
 enable_feature(Feature) when is_atom(Feature) ->
-    application:set_env(
-        config,
-        enabled_features,
-        lists:usort([Feature | features()]),
-        [{persistent, true}]
-    ).
+    Map = persistent_term:get({?MODULE, ?FEATURES}, #{}),
+    Map1 = Map#{Feature => true},
+    persistent_term:put({?MODULE, ?FEATURES}, Map1).
 
 disable_feature(Feature) when is_atom(Feature) ->
-    application:set_env(
-        config,
-        enabled_features,
-        features() -- [Feature],
-        [{persistent, true}]
-    ).
+    Map = persistent_term:get({?MODULE, ?FEATURES}, #{}),
+    Map1 = maps:remove(Feature, Map),
+    persistent_term:put({?MODULE, ?FEATURES}, Map1).
+
+is_enabled(Feature) when is_atom(Feature) ->
+    Map = persistent_term:get({?MODULE, ?FEATURES}, #{}),
+    maps:get(Feature, Map, false).
 
 listen_for_changes(CallbackModule, InitialState) ->
     config_listener_mon:subscribe(CallbackModule, InitialState).
diff --git a/src/config/test/config_tests.erl b/src/config/test/config_tests.erl
index 3d3ee9c94..90d430a87 100644
--- a/src/config/test/config_tests.erl
+++ b/src/config/test/config_tests.erl
@@ -651,11 +651,14 @@ should_enable_features() ->
 
     ?assertEqual(ok, config:enable_feature(snek)),
     ?assertEqual([snek], config:features()),
+    ?assert(config:is_enabled(snek)),
 
     ?assertEqual(ok, config:enable_feature(snek)),
     ?assertEqual([snek], config:features()),
 
     ?assertEqual(ok, config:enable_feature(dogo)),
+    ?assert(config:is_enabled(dogo)),
+    ?assert(config:is_enabled(snek)),
     ?assertEqual([dogo, snek], config:features()).
 
 should_disable_features() ->
@@ -666,9 +669,11 @@ should_disable_features() ->
     ?assertEqual([snek], config:features()),
 
     ?assertEqual(ok, config:disable_feature(snek)),
+    ?assertNot(config:is_enabled(snek)),
     ?assertEqual([], config:features()),
 
     ?assertEqual(ok, config:disable_feature(snek)),
+    ?assertNot(config:is_enabled(snek)),
     ?assertEqual([], config:features()).
 
 should_keep_features_on_config_restart() ->
@@ -678,6 +683,7 @@ should_keep_features_on_config_restart() ->
     config:enable_feature(snek),
     ?assertEqual([snek], config:features()),
     with_process_restart(config),
+    ?assert(config:is_enabled(snek)),
     ?assertEqual([snek], config:features()).
 
 should_notify_on_config_reload(Subscription, {_Apps, Pid}) ->
diff --git a/src/couch/src/couch_hash.erl b/src/couch/src/couch_hash.erl
index 842b37423..2eb123603 100644
--- a/src/couch/src/couch_hash.erl
+++ b/src/couch/src/couch_hash.erl
@@ -31,15 +31,27 @@ md5_hash_update(Context, Data) ->
 -else.
 
 md5_hash(Data) ->
-    crypto:hash(md5, Data).
+    case config:is_enabled(fips_mode) of
+        true -> erlang:md5(Data);
+        false -> crypto:hash(md5, Data)
+    end.
 
 md5_hash_final(Context) ->
-    crypto:hash_final(Context).
+    case config:is_enabled(fips_mode) of
+        true -> erlang:md5_final(Context);
+        false -> crypto:hash_final(Context)
+    end.
 
 md5_hash_init() ->
-    crypto:hash_init(md5).
+    case config:is_enabled(fips_mode) of
+        true -> erlang:md5_init();
+        false -> crypto:hash_init(md5)
+    end.
 
 md5_hash_update(Context, Data) ->
-    crypto:hash_update(Context, Data).
+    case config:is_enabled(fips_mode) of
+        true -> erlang:md5_update(Context, Data);
+        false -> crypto:hash_update(Context, Data)
+    end.
 
 -endif.
diff --git a/src/couch/test/eunit/couch_hash_test.erl b/src/couch/test/eunit/couch_hash_test.erl
new file mode 100644
index 000000000..a9e062cb4
--- /dev/null
+++ b/src/couch/test/eunit/couch_hash_test.erl
@@ -0,0 +1,52 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_hash_test).
+
+-include_lib("couch/include/couch_eunit.hrl").
+
+-define(XY_HASH, <<62, 68, 16, 113, 112, 165, 32, 88, 42, 222, 82, 47, 167, 60, 29, 21>>).
+
+couch_hash_test_() ->
+    {
+        foreach,
+        fun setup/0,
+        fun teardown/1,
+        [
+            ?TDEF_FE(t_fips_disabled),
+            ?TDEF_FE(t_fips_enabled)
+        ]
+    }.
+
+setup() ->
+    Ctx = test_util:start_couch([crypto]),
+    config:disable_feature(fips_mode),
+    Ctx.
+
+teardown(Ctx) ->
+    config:disable_feature(fips_mode),
+    test_util:stop_couch(Ctx).
+
+t_fips_disabled(_) ->
+    ?assertEqual(?XY_HASH, couch_hash:md5_hash(<<"xy">>)),
+    H = couch_hash:md5_hash_init(),
+    H1 = couch_hash:md5_hash_update(H, <<"x">>),
+    H2 = couch_hash:md5_hash_update(H1, <<"y">>),
+    ?assertEqual(?XY_HASH, couch_hash:md5_hash_final(H2)).
+
+t_fips_enabled(_) ->
+    config:enable_feature(fips_mode),
+    ?assertEqual(?XY_HASH, couch_hash:md5_hash(<<"xy">>)),
+    H = couch_hash:md5_hash_init(),
+    H1 = couch_hash:md5_hash_update(H, <<"x">>),
+    H2 = couch_hash:md5_hash_update(H1, <<"y">>),
+    ?assertEqual(?XY_HASH, couch_hash:md5_hash_final(H2)).