From 1b821778f6ffe476e5d53d92998bd6a1f48e0c3e Mon Sep 17 00:00:00 2001
From: Cam Saul <1455846+camsaul@users.noreply.github.com>
Date: Wed, 26 Oct 2022 13:10:48 -0700
Subject: [PATCH] Move `metabase.config.file` code => enterprise directory
 (#26108)

* Move metabase.config.file code => enterprise directory

* Test fix :wrench:

* Fix Kondo error

* Fix Kondo errors

* Remove unused namespace
---
 .clj-kondo/config.edn                         |   6 +-
 .../config_from_file/core.clj                 |  51 ++-----
 .../config_from_file/databases.clj            |  46 ++++++
 .../config_from_file/interface.clj            |  37 +++++
 .../config_from_file/users.clj                |  59 ++++++++
 .../config_from_file/core_test.clj            | 114 +++++++--------
 .../config_from_file/databases_test.clj       |  52 +++++++
 .../config_from_file/users_test.clj           | 137 ++++++++++++++++++
 src/metabase/core.clj                         |   6 +-
 src/metabase/core/config_from_file.clj        |  17 +++
 src/metabase/models/database.clj              |  41 ------
 src/metabase/models/user.clj                  |  55 -------
 test/metabase/email/messages_test.clj         |  17 ++-
 test/metabase/models/database_test.clj        |  47 +-----
 test/metabase/models/user_test.clj            | 130 -----------------
 .../public_settings/premium_features_test.clj |  28 ++--
 test/metabase/test/util.clj                   | 101 +++++++------
 test/metabase/util/encryption_test.clj        |  19 +--
 18 files changed, 521 insertions(+), 442 deletions(-)
 rename src/metabase/config/file.clj => enterprise/backend/src/metabase_enterprise/config_from_file/core.clj (85%)
 create mode 100644 enterprise/backend/src/metabase_enterprise/config_from_file/databases.clj
 create mode 100644 enterprise/backend/src/metabase_enterprise/config_from_file/interface.clj
 create mode 100644 enterprise/backend/src/metabase_enterprise/config_from_file/users.clj
 rename test/metabase/config/file_test.clj => enterprise/backend/test/metabase_enterprise/config_from_file/core_test.clj (60%)
 create mode 100644 enterprise/backend/test/metabase_enterprise/config_from_file/databases_test.clj
 create mode 100644 enterprise/backend/test/metabase_enterprise/config_from_file/users_test.clj
 create mode 100644 src/metabase/core/config_from_file.clj

diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn
index 3449b779739..853a8d409d6 100644
--- a/.clj-kondo/config.edn
+++ b/.clj-kondo/config.edn
@@ -271,7 +271,7 @@
     metabase.plugins.initialize                                     plugins.init
     metabase.public-settings                                        public-settings
     metabase.public-settings.premium-features                       premium-features
-    metabase.pulse                                                  pulse              ; NB some conflicts with metabase.models.pulse
+    metabase.pulse                                                  pulse ; NB some conflicts with metabase.models.pulse
     metabase.pulse.markdown                                         markdown
     metabase.pulse.render                                           render
     metabase.pulse.render.body                                      body
@@ -581,9 +581,10 @@
    metabase.test.data/run-mbql-query                                                                                         hooks.metabase.test.data/mbql-query
    metabase.test.util.async/with-chans                                                                                       hooks.common/let-with-optional-value-for-last-binding
    metabase.test.util.async/with-open-channels                                                                               hooks.common/let-with-optional-value-for-last-binding
+   metabase.test.util.log/with-log-level                                                                                     hooks.common/with-ignored-first-arg
+   metabase.test.util.log/with-log-messages-for-level                                                                        hooks.common/with-ignored-first-arg
    metabase.test.util/discard-setting-changes                                                                                hooks.common/with-ignored-first-arg
    metabase.test.util/with-column-remappings                                                                                 hooks.common/with-ignored-first-arg
-   metabase.test.util/with-log-level                                                                                         hooks.common/with-ignored-first-arg
    metabase.test.util/with-non-admin-groups-no-root-collection-perms                                                         hooks.common/do*
    metabase.test.util/with-temp-file                                                                                         hooks.metabase.test.util/with-temp-file
    metabase.test.util/with-temporary-setting-values                                                                          hooks.metabase.test.util/with-temporary-setting-values
@@ -597,6 +598,7 @@
    metabase.test/with-column-remappings                                                                                      hooks.common/with-ignored-first-arg
    metabase.test/with-group                                                                                                  hooks.common/let-one-with-optional-value
    metabase.test/with-log-level                                                                                              hooks.common/with-ignored-first-arg
+   metabase.test/with-log-messages-for-level                                                                                 hooks.common/with-ignored-first-arg
    metabase.test/with-non-admin-groups-no-root-collection-perms                                                              hooks.common/do*
    metabase.test/with-temp                                                                                                   hooks.toucan.util.test/with-temp
    metabase.test/with-temp*                                                                                                  hooks.toucan.util.test/with-temp*
diff --git a/src/metabase/config/file.clj b/enterprise/backend/src/metabase_enterprise/config_from_file/core.clj
similarity index 85%
rename from src/metabase/config/file.clj
rename to enterprise/backend/src/metabase_enterprise/config_from_file/core.clj
index 7a329cd453b..b363f6f36ab 100644
--- a/src/metabase/config/file.clj
+++ b/enterprise/backend/src/metabase_enterprise/config_from_file/core.clj
@@ -1,5 +1,5 @@
 (ns ^{:added "0.45.0"}
- metabase.config.file
+ metabase-enterprise.config-from-file.core
   "Support for initializing Metabase with configuration from a `config.yml` file located in the current working
   directory. See https://github.com/metabase/metabase/issues/2052 for more information.
 
@@ -99,6 +99,9 @@
    [clojure.tools.logging :as log]
    [clojure.walk :as walk]
    [environ.core :as env]
+   [metabase-enterprise.config-from-file.databases]
+   [metabase-enterprise.config-from-file.interface :as config-from-file.i]
+   [metabase-enterprise.config-from-file.users]
    [metabase.driver.common.parameters]
    [metabase.driver.common.parameters.parse :as params.parse]
    [metabase.util :as u]
@@ -106,32 +109,22 @@
    [metabase.util.i18n :refer [trs]]
    [yaml.core :as yaml]))
 
-(comment metabase.driver.common.parameters/keep-me)
+(comment
+  ;; for parameter parsing
+  metabase.driver.common.parameters/keep-me
+  ;; for `:databases:` section code
+  metabase-enterprise.config-from-file.databases/keep-me
+  ;; for `users:` section code
+  metabase-enterprise.config-from-file.users/keep-me)
 
 (set! *warn-on-reflection* true)
 
-(defmulti section-spec
-  "Spec that should be used to validate the config section with `section-name`, e.g. `:users`. Default spec
-  is [[any?]].
-
-  Sections are validated BEFORE template expansion, so as to avoid leaking any sensitive values in spec errors. Write
-  your specs accordingly!
-
-  Implementations of this method live in other namespaces. For example, the section spec for the `:users` section
-  lives in [[metabase.models.user]]."
-  {:arglists '([section-name])}
-  keyword)
-
-(defmethod section-spec :default
-  [_section-name]
-  any?)
-
 (s/def :metabase.config.file.config/config
   (s/and
    map?
    (fn validate-section-configs [m]
      (doseq [[section-name section-config] m
-             :let [spec (section-spec section-name)]]
+             :let [spec (config-from-file.i/section-spec section-name)]]
        (s/assert* spec section-config))
      true)))
 
@@ -241,28 +234,14 @@
     (s/assert* ::config m)
     (expand-templates m)))
 
-(defmulti initialize-section!
-  "Execute initialization code for the section of the init config file with the key `section-name` and value
-  `section-config`.
-
-  Implementations of this method live in other namespaces, for example the method for the `:users` section (to
-  initialize Users) lives in [[metabase.models.user]]."
-  {:arglists '([section-name section-config])}
-  (fn [section-name _section-config]
-    (keyword section-name)))
-
-;;; if we don't know how to initialize a particular section, just log a warning and proceed. This way we can be
-;;; forward-compatible and handle sections that might be unknown in a particular version of Metabase.
-(defmethod initialize-section! :default
-  [section-name _section-config]
-  (log/warn (u/colorize :yellow (trs "Ignoring unknown config section {0}." (pr-str section-name)))))
-
 (defn ^{:added "0.45.0"} initialize!
   "Initialize Metabase according to the directives in the config file, if it exists."
   []
+  ;; TODO -- this should only do anything if we have an appropriate token (we should get a token for testing this before
+  ;; enabling that check tho)
   (when-let [m (config)]
     (doseq [[section-name section-config] (:config m)]
       (log/info (u/colorize :magenta (trs "Initializing {0} from config file..." section-name)) (u/emoji "🗄️"))
-      (initialize-section! section-name section-config))
+      (config-from-file.i/initialize-section! section-name section-config))
     (log/info (u/colorize :magenta (trs "Done initializing from file.")) (u/emoji "🗄️")))
   :ok)
diff --git a/enterprise/backend/src/metabase_enterprise/config_from_file/databases.clj b/enterprise/backend/src/metabase_enterprise/config_from_file/databases.clj
new file mode 100644
index 00000000000..73bb69c1764
--- /dev/null
+++ b/enterprise/backend/src/metabase_enterprise/config_from_file/databases.clj
@@ -0,0 +1,46 @@
+(ns metabase-enterprise.config-from-file.databases
+  (:require
+   [clojure.spec.alpha :as s]
+   [clojure.tools.logging :as log]
+   [metabase-enterprise.config-from-file.interface :as config-from-file.i]
+   [metabase.driver.util :as driver.u]
+   [metabase.models.database :refer [Database]]
+   [metabase.util :as u]
+   [metabase.util.i18n :refer [trs]]
+   [toucan.db :as db]))
+
+(s/def :metabase-enterprise.config-from-file.databases.config-file-spec/name
+  string?)
+
+(s/def :metabase-enterprise.config-from-file.databases.config-file-spec/engine
+  string?)
+
+(s/def :metabase-enterprise.config-from-file.databases.config-file-spec/details
+  map?)
+
+(s/def ::config-file-spec
+  (s/keys :req-un [:metabase-enterprise.config-from-file.databases.config-file-spec/engine
+                   :metabase-enterprise.config-from-file.databases.config-file-spec/name
+                   :metabase-enterprise.config-from-file.databases.config-file-spec/details]))
+
+(defmethod config-from-file.i/section-spec :databases
+  [_section]
+  (s/spec (s/* ::config-file-spec)))
+
+(defn- init-from-config-file!
+  [database]
+  ;; assert that we are able to connect to this Database. Otherwise, throw an Exception.
+  (driver.u/can-connect-with-details? (keyword (:engine database)) (:details database) :throw-exceptions)
+  (if-let [existing-database-id (db/select-one-id Database :engine (:engine database), :name (:name database))]
+    (do
+      (log/info (u/colorize :blue (trs "Updating Database {0} {1}" (:engine database) (pr-str (:name database)))))
+      (db/update! Database existing-database-id database))
+    (do
+      (log/info (u/colorize :green (trs "Creating new {0} Database {1}" (:engine database) (pr-str (:name database)))))
+      (let [db (db/insert! Database database)]
+        ((requiring-resolve 'metabase.sync/sync-database!) db)))))
+
+(defmethod config-from-file.i/initialize-section! :databases
+  [_section-name databases]
+  (doseq [database databases]
+    (init-from-config-file! database)))
diff --git a/enterprise/backend/src/metabase_enterprise/config_from_file/interface.clj b/enterprise/backend/src/metabase_enterprise/config_from_file/interface.clj
new file mode 100644
index 00000000000..9fa27621240
--- /dev/null
+++ b/enterprise/backend/src/metabase_enterprise/config_from_file/interface.clj
@@ -0,0 +1,37 @@
+(ns metabase-enterprise.config-from-file.interface
+  (:require
+   [clojure.tools.logging :as log]
+   [metabase.util :as u]
+   [metabase.util.i18n :refer [trs]]))
+
+(defmulti section-spec
+  "Spec that should be used to validate the config section with `section-name`, e.g. `:users`. Default spec
+  is [[any?]].
+
+  Sections are validated BEFORE template expansion, so as to avoid leaking any sensitive values in spec errors. Write
+  your specs accordingly!
+
+  Implementations of this method live in other namespaces. For example, the section spec for the `:users` section
+  lives in [[metabase.models.user]]."
+  {:arglists '([section-name])}
+  keyword)
+
+(defmethod section-spec :default
+  [_section-name]
+  any?)
+
+(defmulti initialize-section!
+  "Execute initialization code for the section of the init config file with the key `section-name` and value
+  `section-config`.
+
+  Implementations of this method live in other namespaces, for example the method for the `:users` section (to
+  initialize Users) lives in [[metabase.models.user]]."
+  {:arglists '([section-name section-config])}
+  (fn [section-name _section-config]
+    (keyword section-name)))
+
+;;; if we don't know how to initialize a particular section, just log a warning and proceed. This way we can be
+;;; forward-compatible and handle sections that might be unknown in a particular version of Metabase.
+(defmethod initialize-section! :default
+  [section-name _section-config]
+  (log/warn (u/colorize :yellow (trs "Ignoring unknown config section {0}." (pr-str section-name)))))
diff --git a/enterprise/backend/src/metabase_enterprise/config_from_file/users.clj b/enterprise/backend/src/metabase_enterprise/config_from_file/users.clj
new file mode 100644
index 00000000000..a2fe138ab3c
--- /dev/null
+++ b/enterprise/backend/src/metabase_enterprise/config_from_file/users.clj
@@ -0,0 +1,59 @@
+(ns metabase-enterprise.config-from-file.users
+  (:require
+   [clojure.spec.alpha :as s]
+   [clojure.tools.logging :as log]
+   [metabase-enterprise.config-from-file.interface :as config-from-file.i]
+   [metabase.models.user :refer [User]]
+   [metabase.util :as u]
+   [metabase.util.i18n :as i18n :refer [trs]]
+   [toucan.db :as db]))
+
+(s/def :metabase-enterprise.config-from-file.users.config-file-spec/first_name
+  string?)
+
+(s/def :metabase-enterprise.config-from-file.users.config-file-spec/last_name
+  string?)
+
+(s/def :metabase-enterprise.config-from-file.users.config-file-spec/password
+  string?)
+
+(s/def :metabase-enterprise.config-from-file.users.config-file-spec/email
+  string?)
+
+(s/def ::config-file-spec
+  (s/keys :req-un [:metabase-enterprise.config-from-file.users.config-file-spec/first_name
+                   :metabase-enterprise.config-from-file.users.config-file-spec/last_name
+                   :metabase-enterprise.config-from-file.users.config-file-spec/password
+                   :metabase-enterprise.config-from-file.users.config-file-spec/email]))
+
+(defmethod config-from-file.i/section-spec :users
+  [_section]
+  (s/spec (s/* ::config-file-spec)))
+
+(defn- init-from-config-file-is-first-user?
+  "For [[init-from-config-file!]]: `true` if this the first User being created for this instance. If so, we will ALWAYS
+  create that User as a superuser, regardless of what is specified in the config file. (It doesn't make sense to
+  create the first User as anything other than a superuser)."
+  []
+  (zero? (db/count User)))
+
+(defn- init-from-config-file!
+  [user]
+  ;; TODO -- if this is the FIRST user, we should probably make them a superuser, right?
+  (if-let [existing-user-id (db/select-one-id User :email (:email user))]
+    (do
+      (log/info (u/colorize :blue (trs "Updating User with email {0}" (pr-str (:email user)))))
+      (db/update! User existing-user-id user))
+    ;; create a new user. If they are the first User, force them to be an admin.
+    (let [user (cond-> user
+                 (init-from-config-file-is-first-user?) (assoc :is_superuser true))]
+      (log/info (u/colorize :green (trs "Creating the first User for this instance. The first user is always created as an admin.")))
+      (log/info (u/colorize :green (trs "Creating new User {0} with email {1}"
+                                        (pr-str (str (:first_name user) \space (:last_name user)))
+                                        (pr-str (:email user)))))
+      (db/insert! User user))))
+
+(defmethod config-from-file.i/initialize-section! :users
+  [_section-name users]
+  (doseq [user users]
+    (init-from-config-file! user)))
diff --git a/test/metabase/config/file_test.clj b/enterprise/backend/test/metabase_enterprise/config_from_file/core_test.clj
similarity index 60%
rename from test/metabase/config/file_test.clj
rename to enterprise/backend/test/metabase_enterprise/config_from_file/core_test.clj
index ca677ea12a5..1cbecb6d9a9 100644
--- a/test/metabase/config/file_test.clj
+++ b/enterprise/backend/test/metabase_enterprise/config_from_file/core_test.clj
@@ -1,15 +1,15 @@
-(ns metabase.config.file-test
+(ns metabase-enterprise.config-from-file.core-test
   (:require
    [clojure.string :as str]
    [clojure.test :refer :all]
    [clojure.walk :as walk]
-   [metabase.config.file :as config.file]
+   [metabase-enterprise.config-from-file.core :as config-from-file]
    [metabase.test :as mt]
    [metabase.util :as u]
    [yaml.core :as yaml]))
 
 (use-fixtures :each (fn [thunk]
-                      (binding [config.file/*supported-versions* {:min 1.0, :max 1.999}]
+                      (binding [config-from-file/*supported-versions* {:min 1.0, :max 1.999}]
                         (thunk))))
 
 (defn- re-quote [^String s]
@@ -23,46 +23,46 @@
   (testing "Specify a custom path and read from YAML"
     (mt/with-temp-file [filename "temp-config-file.yml"]
       (spit filename (yaml/generate-string mock-yaml))
-      (binding [config.file/*env* (assoc @#'config.file/*env* :mb-config-file-path filename)]
+      (binding [config-from-file/*env* (assoc @#'config-from-file/*env* :mb-config-file-path filename)]
         (is (= {:version 1
                 :config  {:settings {:my-setting "abc123"}}}
-               (#'config.file/config))))))
+               (#'config-from-file/config))))))
   (testing "Support overriding config with dynamic var for mocking purposes"
-    (binding [config.file/*config* mock-yaml]
+    (binding [config-from-file/*config* mock-yaml]
       (is (= {:version 1
               :config  {:settings {:my-setting "abc123"}}}
-             (#'config.file/config))))))
+             (#'config-from-file/config))))))
 
 (deftest ^:parallel validate-config-test
   (testing "Config should throw an error"
     (testing "if it is not a map"
-      (binding [config.file/*config* [1 2 3 4]]
+      (binding [config-from-file/*config* [1 2 3 4]]
         (is (thrown-with-msg?
              clojure.lang.ExceptionInfo
              (re-quote "failed: map?")
-             (#'config.file/config)))))
+             (#'config-from-file/config)))))
     (testing "if version"
       (testing "is not included"
-        (binding [config.file/*config* {:config {:settings {}}}]
+        (binding [config-from-file/*config* {:config {:settings {}}}]
           (is (thrown-with-msg?
                clojure.lang.ExceptionInfo
                (re-quote "failed: (contains? % :version)")
-               (#'config.file/config)))))
+               (#'config-from-file/config)))))
       (testing "is unsupported"
         (testing "because it is too old"
-          (binding [config.file/*supported-versions* {:min 2.0, :max 3.0}
-                    config.file/*config*             {:version 1.0, :config {}}]
+          (binding [config-from-file/*supported-versions* {:min 2.0, :max 3.0}
+                    config-from-file/*config*             {:version 1.0, :config {}}]
             (is (thrown-with-msg?
                  clojure.lang.ExceptionInfo
                  (re-quote "failed: supported-version?")
-                 (#'config.file/config)))))
+                 (#'config-from-file/config)))))
         (testing "because it is too new"
-          (binding [config.file/*supported-versions* {:min 2.0, :max 3.0}
-                    config.file/*config*             {:version 4.0, :config {}}]
+          (binding [config-from-file/*supported-versions* {:min 2.0, :max 3.0}
+                    config-from-file/*config*             {:version 4.0, :config {}}]
             (is (thrown-with-msg?
                  clojure.lang.ExceptionInfo
                  (re-quote "failed: supported-version?")
-                 (#'config.file/config)))))))))
+                 (#'config-from-file/config)))))))))
 
 (defn- mock-config-with-setting [s]
   {:version 1.0, :config {:settings {:my-setting s}}})
@@ -70,8 +70,8 @@
 (deftest ^:parallel expand-template-forms-test
   (testing "Ignore single curly brackets, or brackets with spaces between them"
     (are [s] (= (mock-config-with-setting s)
-                (binding [config.file/*config* (mock-config-with-setting s)]
-                  (#'config.file/config)))
+                (binding [config-from-file/*config* (mock-config-with-setting s)]
+                  (#'config-from-file/config)))
       "{}}"
       "{}}"
       "{ {}}"))
@@ -79,8 +79,8 @@
     (are [template error-pattern] (thrown-with-msg?
                                    clojure.lang.ExceptionInfo
                                    error-pattern
-                                   (binding [config.file/*config* (mock-config-with-setting template)]
-                                     (#'config.file/config)))
+                                   (binding [config-from-file/*config* (mock-config-with-setting template)]
+                                     (#'config-from-file/config)))
       ;; {{ without a corresponding }}
       "{{}"                        (re-quote "Invalid query: found [[ or {{ with no matching ]] or }}")
       "{{} }"                      (re-quote "Invalid query: found [[ or {{ with no matching ]] or }}")
@@ -93,36 +93,36 @@
 
 (deftest ^:parallel recursive-template-form-expansion-test
   (testing "Recursive expansion is unsupported, for now."
-    (binding [config.file/*env*    (assoc @#'config.file/*env* :x "{{env Y}}", :y "Y")
-              config.file/*config* (mock-config-with-setting "{{env X}}")]
+    (binding [config-from-file/*env*    (assoc @#'config-from-file/*env* :x "{{env Y}}", :y "Y")
+              config-from-file/*config* (mock-config-with-setting "{{env X}}")]
       (is (= (mock-config-with-setting "{{env Y}}")
-                 (#'config.file/config))))))
+                 (#'config-from-file/config))))))
 
 (deftest ^:parallel expand-template-env-var-values-test
   (testing "env var values"
-    (binding [config.file/*env* (assoc @#'config.file/*env* :config-file-bird-name "Parrot Hilton")]
+    (binding [config-from-file/*env* (assoc @#'config-from-file/*env* :config-file-bird-name "Parrot Hilton")]
       (testing "Nothing weird"
-        (binding [config.file/*config* (mock-config-with-setting "{{env CONFIG_FILE_BIRD_NAME}}")]
+        (binding [config-from-file/*config* (mock-config-with-setting "{{env CONFIG_FILE_BIRD_NAME}}")]
           (is (= (mock-config-with-setting "Parrot Hilton")
-                 (#'config.file/config)))))
+                 (#'config-from-file/config)))))
       (testing "Should handle multiple templates in one string"
-        (binding [config.file/*config* (mock-config-with-setting "{{env CONFIG_FILE_BIRD_NAME}}-{{env CONFIG_FILE_BIRD_NAME}}")]
+        (binding [config-from-file/*config* (mock-config-with-setting "{{env CONFIG_FILE_BIRD_NAME}}-{{env CONFIG_FILE_BIRD_NAME}}")]
           (is (= (mock-config-with-setting "Parrot Hilton-Parrot Hilton")
-                 (#'config.file/config)))))
+                 (#'config-from-file/config)))))
       (testing "Ignore whitespace inside the template brackets"
-        (binding [config.file/*config* (mock-config-with-setting "{{  env CONFIG_FILE_BIRD_NAME  }}")]
+        (binding [config-from-file/*config* (mock-config-with-setting "{{  env CONFIG_FILE_BIRD_NAME  }}")]
           (is (= (mock-config-with-setting "Parrot Hilton")
-                 (#'config.file/config)))))
+                 (#'config-from-file/config)))))
       (testing "Ignore excess brackets"
         (are [template expected] (= (mock-config-with-setting expected)
-                                    (binding [config.file/*config* (mock-config-with-setting template)]
-                                      (#'config.file/config)))
+                                    (binding [config-from-file/*config* (mock-config-with-setting template)]
+                                      (#'config-from-file/config)))
           "{{{env CONFIG_FILE_BIRD_NAME}}" "{Parrot Hilton"
           "{{env CONFIG_FILE_BIRD_NAME}}}" "Parrot Hilton}"))
       (testing "handle lisp-case/snake-case and case variations"
-        (binding [config.file/*config* (mock-config-with-setting "{{env config-file-bird-name}}")]
+        (binding [config-from-file/*config* (mock-config-with-setting "{{env config-file-bird-name}}")]
           (is (= (mock-config-with-setting "Parrot Hilton")
-                 (#'config.file/config))))))))
+                 (#'config-from-file/config))))))))
 
 (deftest ^:parallel expand-template-env-var-values-validation-test
   (testing "(config) should walk the config map and expand {{template}} forms"
@@ -131,8 +131,8 @@
         (are [template error-pattern] (thrown-with-msg?
                                        clojure.lang.ExceptionInfo
                                        error-pattern
-                                       (binding [config.file/*config* (mock-config-with-setting template)]
-                                         (#'config.file/config)))
+                                       (binding [config-from-file/*config* (mock-config-with-setting template)]
+                                         (#'config-from-file/config)))
           ;; missing env var name
           "{{env}}"                           #"Insufficient input"
           ;; too many args
@@ -142,51 +142,51 @@
 
 (deftest ^:parallel optional-template-test
   (testing "[[optional {{template}}]] values"
-    (binding [config.file/*env* (assoc @#'config.file/*env* :my-sensitive-password "~~~SeCrEt123~~~")]
+    (binding [config-from-file/*env* (assoc @#'config-from-file/*env* :my-sensitive-password "~~~SeCrEt123~~~")]
       (testing "env var exists"
-        (binding [config.file/*config* (mock-config-with-setting "[[{{env MY_SENSITIVE_PASSWORD}}]]")]
+        (binding [config-from-file/*config* (mock-config-with-setting "[[{{env MY_SENSITIVE_PASSWORD}}]]")]
           (is (= (mock-config-with-setting "~~~SeCrEt123~~~")
-                 (#'config.file/config))))
-        (binding [config.file/*config* (mock-config-with-setting "password__[[{{env MY_SENSITIVE_PASSWORD}}]]")]
+                 (#'config-from-file/config))))
+        (binding [config-from-file/*config* (mock-config-with-setting "password__[[{{env MY_SENSITIVE_PASSWORD}}]]")]
           (is (= (mock-config-with-setting "password__~~~SeCrEt123~~~")
-                 (#'config.file/config))))
+                 (#'config-from-file/config))))
         (testing "with text inside optional brackets before/after the templated part"
-          (binding [config.file/*config* (mock-config-with-setting "[[before__{{env MY_SENSITIVE_PASSWORD}}__after]]")]
+          (binding [config-from-file/*config* (mock-config-with-setting "[[before__{{env MY_SENSITIVE_PASSWORD}}__after]]")]
             (is (= (mock-config-with-setting "before__~~~SeCrEt123~~~__after")
-                   (#'config.file/config))))))
+                   (#'config-from-file/config))))))
       (testing "env var does not exist"
-        (binding [config.file/*config* (mock-config-with-setting "[[{{env MY_OTHER_SENSITIVE_PASSWORD}}]]")]
+        (binding [config-from-file/*config* (mock-config-with-setting "[[{{env MY_OTHER_SENSITIVE_PASSWORD}}]]")]
           (is (= (mock-config-with-setting "")
-                 (#'config.file/config))))
-        (binding [config.file/*config* (mock-config-with-setting "password__[[{{env MY_OTHER_SENSITIVE_PASSWORD}}]]")]
+                 (#'config-from-file/config))))
+        (binding [config-from-file/*config* (mock-config-with-setting "password__[[{{env MY_OTHER_SENSITIVE_PASSWORD}}]]")]
           (is (= (mock-config-with-setting "password__")
-                 (#'config.file/config))))
+                 (#'config-from-file/config))))
         (testing "with text inside optional brackets before/after the templated part"
-          (binding [config.file/*config* (mock-config-with-setting "[[before__{{env MY_OTHER_SENSITIVE_PASSWORD}}__after]]")]
+          (binding [config-from-file/*config* (mock-config-with-setting "[[before__{{env MY_OTHER_SENSITIVE_PASSWORD}}__after]]")]
             (is (= (mock-config-with-setting "")
-                   (#'config.file/config)))))))))
+                   (#'config-from-file/config)))))))))
 
 (deftest initialize-section-test
   (testing "Ignore unknown sections"
-    (binding [config.file/*config* {:version 1.0, :config {:unknown-section {}}}]
-      (let [log-messages (mt/with-log-messages-for-level [metabase.config.file :warn]
+    (binding [config-from-file/*config* {:version 1.0, :config {:unknown-section {}}}]
+      (let [log-messages (mt/with-log-messages-for-level [metabase-enterprise.config-from-file.interface :warn]
                            (is (= :ok
-                                  (config.file/initialize!))))]
+                                  (config-from-file/initialize!))))]
         (is (= [[:warn nil (u/colorize :yellow "Ignoring unknown config section :unknown-section.")]]
                log-messages))))))
 
 (deftest ^:parallel error-validation-do-not-leak-env-vars-test
   (testing "spec errors should not include contents of env vars -- expand templates after spec validation."
-    (binding [config.file/*env*    (assoc @#'config.file/*env* :my-sensitive-password "~~~SeCrEt123~~~")
-              config.file/*config* {:version 1
+    (binding [config-from-file/*env*    (assoc @#'config-from-file/*env* :my-sensitive-password "~~~SeCrEt123~~~")
+              config-from-file/*config* {:version 1
                                     :config  {:users [{:first_name "Cam"
                                                        :last_name  "Era"
                                                        :password   "{{env MY_SENSITIVE_PASSWORD}}"}]}}]
       (is (thrown?
            clojure.lang.ExceptionInfo
-           (#'config.file/config)))
+           (#'config-from-file/config)))
       (try
-        (#'config.file/config)
+        (#'config-from-file/config)
         (catch Throwable e
           (letfn [(contains-password? [form]
                     (let [seen-password? (atom false)]
diff --git a/enterprise/backend/test/metabase_enterprise/config_from_file/databases_test.clj b/enterprise/backend/test/metabase_enterprise/config_from_file/databases_test.clj
new file mode 100644
index 00000000000..07364f2db80
--- /dev/null
+++ b/enterprise/backend/test/metabase_enterprise/config_from_file/databases_test.clj
@@ -0,0 +1,52 @@
+(ns metabase-enterprise.config-from-file.databases-test
+  (:require
+   [clojure.test :refer :all]
+   [metabase-enterprise.config-from-file.core :as config-from-file]
+   [metabase.db.connection :as mdb.connection]
+   [metabase.models :refer [Database Table]]
+   [metabase.test :as mt]
+   [metabase.util :as u]
+   [toucan.db :as db]))
+
+(deftest init-from-config-file-test
+  (let [db-type     (mdb.connection/db-type)
+        original-db (mt/with-driver db-type (mt/db))]
+    (try
+      (binding [config-from-file/*supported-versions* {:min 1, :max 1}
+                config-from-file/*config*             {:version 1
+                                                       :config  {:databases [{:name    "init-from-config-file-test/test-data"
+                                                                              :engine  (name db-type)
+                                                                              :details (:details original-db)}]}}]
+        (testing "Create a Database if it does not already exist"
+          (is (= :ok
+                 (config-from-file/initialize!)))
+          (let [db (db/select-one Database :name "init-from-config-file-test/test-data")]
+            (is (partial= {:engine db-type}
+                          db))
+            (is (= 1
+                   (db/count Database :name "init-from-config-file-test/test-data")))
+            (testing "do not duplicate if Database already exists"
+              (is (= :ok
+                     (config-from-file/initialize!)))
+              (is (= 1
+                     (db/count Database :name "init-from-config-file-test/test-data")))
+              (is (partial= {:engine db-type}
+                            (db/select-one Database :name "init-from-config-file-test/test-data"))))
+            (testing "Database should have been synced"
+              (is (= (db/count Table :db_id (u/the-id original-db))
+                     (db/count Table :db_id (u/the-id db))))))))
+      (finally
+        (db/delete! Database :name "init-from-config-file-test/test-data")))))
+
+(deftest ^:parallel init-from-config-file-connection-validation-test
+  (testing "Validate connection details when creating a Database from a config file, and error if they are invalid"
+    (binding [config-from-file/*supported-versions* {:min 1, :max 1}
+              config-from-file/*config*             {:version 1
+                                                     :config  {:databases [{:name    "inist-from-config-file-test/test-data-in-memory"
+                                                                            :engine  "h2"
+                                                                            :details {:db "mem:some-in-memory-db"}}]}}]
+      (testing "Create a Database if it does not already exist"
+        (is (thrown-with-msg?
+             clojure.lang.ExceptionInfo
+             #"Database cannot be found\."
+             (config-from-file/initialize!)))))))
diff --git a/enterprise/backend/test/metabase_enterprise/config_from_file/users_test.clj b/enterprise/backend/test/metabase_enterprise/config_from_file/users_test.clj
new file mode 100644
index 00000000000..60fc6c8c76b
--- /dev/null
+++ b/enterprise/backend/test/metabase_enterprise/config_from_file/users_test.clj
@@ -0,0 +1,137 @@
+(ns metabase-enterprise.config-from-file.users-test
+  (:require
+   [clojure.test :refer :all]
+   [metabase-enterprise.config-from-file.core :as config-from-file]
+   [metabase-enterprise.config-from-file.users :as config-from-file.users]
+   [metabase.models :refer [User]]
+   [metabase.util.password :as u.password]
+   [toucan.db :as db]))
+
+(deftest init-from-config-file-test
+  (try
+    (binding [config-from-file/*supported-versions* {:min 1, :max 1}
+              config-from-file/*config*             {:version 1
+                                                     :config  {:users [{:first_name "Cam"
+                                                                        :last_name  "Era"
+                                                                        :email      "cam+config-file-test@metabase.com"
+                                                                        :password   "2cans"}]}}]
+      (testing "Create a User if it does not already exist"
+        (is (= :ok
+               (config-from-file/initialize!)))
+        (is (partial= {:first_name "Cam"
+                       :last_name  "Era"
+                       :email      "cam+config-file-test@metabase.com"}
+                      (db/select-one User :email "cam+config-file-test@metabase.com")))
+        (is (= 1
+               (db/count User :email "cam+config-file-test@metabase.com"))))
+      (testing "upsert if User already exists"
+        (binding [config-from-file/*config* {:version 1
+                                             :config  {:users [{:first_name "Cam"
+                                                                :last_name  "Saul"
+                                                                :email      "cam+config-file-test@metabase.com"
+                                                                :password   "2cans"}]}}]
+          (is (= :ok
+                 (config-from-file/initialize!)))
+          (is (= 1
+                 (db/count User :email "cam+config-file-test@metabase.com")))
+          (is (partial= {:first_name "Cam"
+                         :last_name  "Saul"
+                         :email      "cam+config-file-test@metabase.com"}
+                        (db/select-one User :email "cam+config-file-test@metabase.com"))))))
+    (finally
+      (db/delete! User :email "cam+config-file-test@metabase.com"))))
+
+(deftest init-from-config-file-force-admin-for-first-user-test
+  (testing "If this is the first user being created, always make the user a superuser regardless of what is specified"
+    (try
+      (binding [config-from-file/*supported-versions* {:min 1, :max 1}]
+        (testing "Create the first User"
+          (binding [config-from-file/*config* {:version 1
+                                               :config  {:users [{:first_name   "Cam"
+                                                                  :last_name    "Era"
+                                                                  :email        "cam+config-file-admin-test@metabase.com"
+                                                                  :password     "2cans"
+                                                                  :is_superuser false}]}}]
+            (with-redefs [config-from-file.users/init-from-config-file-is-first-user? (constantly true)]
+              (is (= :ok
+                     (config-from-file/initialize!)))
+              (is (partial= {:first_name   "Cam"
+                             :last_name    "Era"
+                             :email        "cam+config-file-admin-test@metabase.com"
+                             :is_superuser true}
+                            (db/select-one User :email "cam+config-file-admin-test@metabase.com")))
+              (is (= 1
+                     (db/count User :email "cam+config-file-admin-test@metabase.com"))))))
+        (testing "Create the another User, DO NOT force them to be an admin"
+          (binding [config-from-file/*config* {:version 1
+                                               :config  {:users [{:first_name   "Cam"
+                                                                  :last_name    "Saul"
+                                                                  :email        "cam+config-file-admin-test-2@metabase.com"
+                                                                  :password     "2cans"
+                                                                  :is_superuser false}]}}]
+            (is (= :ok
+                   (config-from-file/initialize!)))
+            (is (partial= {:first_name   "Cam"
+                           :last_name    "Saul"
+                           :email        "cam+config-file-admin-test-2@metabase.com"
+                           :is_superuser false}
+                          (db/select-one User :email "cam+config-file-admin-test-2@metabase.com")))
+            (is (= 1
+                   (db/count User :email "cam+config-file-admin-test-2@metabase.com"))))))
+      (finally (db/delete! User :email [:in #{"cam+config-file-admin-test@metabase.com"
+                                              "cam+config-file-admin-test-2@metabase.com"}])))))
+
+(deftest init-from-config-file-env-var-for-password-test
+  (testing "Ensure that we can set User password using {{env ...}} templates"
+    (try
+      (binding [config-from-file/*supported-versions* {:min 1, :max 1}
+                config-from-file/*config*             {:version 1
+                                                       :config  {:users [{:first_name "Cam"
+                                                                          :last_name  "Era"
+                                                                          :email      "cam+config-file-password-test@metabase.com"
+                                                                          :password   "{{env USER_PASSWORD}}"}]}}
+                config-from-file/*env*                (assoc @#'config-from-file/*env* :user-password "1234cans")]
+        (testing "Create a User if it does not already exist"
+          (is (= :ok
+                 (config-from-file/initialize!)))
+          (let [user (db/select-one [User :first_name :last_name :email :password_salt :password]
+                       :email "cam+config-file-password-test@metabase.com")]
+            (is (partial= {:first_name "Cam"
+                           :last_name  "Era"
+                           :email      "cam+config-file-password-test@metabase.com"}
+                          user))
+            (is (u.password/verify-password "1234cans" (:password_salt user) (:password user))))))
+      (finally
+        (db/delete! User :email "cam+config-file-password-test@metabase.com")))))
+
+(deftest ^:parallel init-from-config-file-validation-test
+  (binding [config-from-file/*supported-versions* {:min 1, :max 1}]
+    (are [user error-pattern] (thrown-with-msg?
+                               clojure.lang.ExceptionInfo
+                               error-pattern
+                               (binding [config-from-file/*config* {:version 1
+                                                                    :config  {:users [user]}}]
+                                 (#'config-from-file/config)))
+      ;; missing email
+      {:first_name "Cam"
+       :last_name  "Era"
+       :password   "2cans"}
+      (re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :email)"))
+
+      ;; missing first name
+      {:last_name  "Era"
+       :email      "cam+config-file-admin-test@metabase.com"
+       :password   "2cans"}
+      (re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :first_name)"))
+
+      ;; missing last name
+      {:first_name "Cam"
+       :email      "cam+config-file-admin-test@metabase.com"
+       :password   "2cans"}
+      (re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :last_name)"))
+
+      ;; missing password
+      {:first_name "Cam"
+       :last_name  "Era"
+       :email      "cam+config-file-test@metabase.com"}
+      (re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :password)")))))
diff --git a/src/metabase/core.clj b/src/metabase/core.clj
index 41dfb136e54..aab128843e9 100644
--- a/src/metabase/core.clj
+++ b/src/metabase/core.clj
@@ -5,7 +5,7 @@
             [java-time :as t]
             [metabase.analytics.prometheus :as prometheus]
             [metabase.config :as config]
-            [metabase.config.file :as config.file]
+            [metabase.core.config-from-file :as config-from-file]
             [metabase.core.initialization-status :as init-status]
             [metabase.db :as mdb]
             metabase.driver.h2
@@ -105,8 +105,8 @@
     (log/info (trs "Setting up prometheus metrics"))
     (prometheus/setup!)
     (init-status/set-progress! 0.6))
-  ;; initialize Metabase from an `config.yml` file as needed.
-  (config.file/initialize!)
+  ;; initialize Metabase from an `config.yml` file if present (Enterprise Editionâ„¢ only)
+  (config-from-file/init-from-file-if-code-available!)
   (init-status/set-progress! 0.65)
   ;; Bootstrap the event system
   (events/initialize-events!)
diff --git a/src/metabase/core/config_from_file.clj b/src/metabase/core/config_from_file.clj
new file mode 100644
index 00000000000..cc1840c9db4
--- /dev/null
+++ b/src/metabase/core/config_from_file.clj
@@ -0,0 +1,17 @@
+(ns metabase.core.config-from-file
+  (:require
+   [clojure.tools.logging :as log]
+   [metabase.plugins.classloader :as classloader]))
+
+(defn init-from-file-if-code-available!
+  "Shim for running the config-from-file code, used by [[metabase.core]]. The config-from-file code only ships in the
+  Enterprise Editionâ„¢ JAR, so this checks whether the namespace exists, and if it does,
+  invokes [[metabase-enterprise.config-from-file.core/initialize!]]; otherwise, this no-ops."
+  []
+  (when (try
+          (classloader/require 'metabase-enterprise.config-from-file.core)
+          :ok
+          (catch Throwable _
+            (log/debug "metabase-enterprise.config-from-file.core not available; cannot initialize from file.")
+            nil))
+    ((resolve 'metabase-enterprise.config-from-file.core/initialize!))))
diff --git a/src/metabase/models/database.clj b/src/metabase/models/database.clj
index c6a0cea1350..25a91ef7fa3 100644
--- a/src/metabase/models/database.clj
+++ b/src/metabase/models/database.clj
@@ -1,10 +1,8 @@
 (ns metabase.models.database
   (:require
    [cheshire.generate :refer [add-encoder encode-map]]
-   [clojure.spec.alpha :as s]
    [clojure.tools.logging :as log]
    [medley.core :as m]
-   [metabase.config.file :as config.file]
    [metabase.db.util :as mdb.u]
    [metabase.driver :as driver]
    [metabase.driver.impl :as driver.impl]
@@ -314,42 +312,3 @@
         (not (:details database)) (assoc :details "{}"))
       serdes.base/load-xform-basics
       (update :creator_id serdes.util/import-user)))
-
-
-;;;; initialization from files
-
-(s/def :metabase.models.database.config-file-spec/name
-  string?)
-
-(s/def :metabase.models.database.config-file-spec/engine
-  string?)
-
-(s/def :metabase.models.database.config-file-spec/details
-  map?)
-
-(s/def ::config-file-spec
-  (s/keys :req-un [:metabase.models.database.config-file-spec/engine
-                   :metabase.models.database.config-file-spec/name
-                   :metabase.models.database.config-file-spec/details]))
-
-(defmethod config.file/section-spec :databases
-  [_section]
-  (s/spec (s/* ::config-file-spec)))
-
-(defn- init-from-config-file!
-  [database]
-  ;; assert that we are able to connect to this Database. Otherwise, throw an Exception.
-  (driver.u/can-connect-with-details? (keyword (:engine database)) (:details database) :throw-exceptions)
-  (if-let [existing-database-id (db/select-one-id Database :engine (:engine database), :name (:name database))]
-    (do
-      (log/info (u/colorize :blue (trs "Updating Database {0} {1}" (:engine database) (pr-str (:name database)))))
-      (db/update! Database existing-database-id database))
-    (do
-      (log/info (u/colorize :green (trs "Creating new {0} Database {1}" (:engine database) (pr-str (:name database)))))
-      (let [db (db/insert! Database database)]
-        ((requiring-resolve 'metabase.sync/sync-database!) db)))))
-
-(defmethod config.file/initialize-section! :databases
-  [_section-name databases]
-  (doseq [database databases]
-    (init-from-config-file! database)))
diff --git a/src/metabase/models/user.clj b/src/metabase/models/user.clj
index b423dab5f80..1c7f85572ac 100644
--- a/src/metabase/models/user.clj
+++ b/src/metabase/models/user.clj
@@ -1,10 +1,8 @@
 (ns metabase.models.user
   (:require
    [clojure.data :as data]
-   [clojure.spec.alpha :as s]
    [clojure.string :as str]
    [clojure.tools.logging :as log]
-   [metabase.config.file :as config.file]
    [metabase.models.collection :as collection]
    [metabase.models.permissions :as perms]
    [metabase.models.permissions-group :as perms-group]
@@ -367,56 +365,3 @@
        (doseq [group-id to-add]
          (db/insert! PermissionsGroupMembership {:user_id user-id, :group_id group-id}))))
     true))
-
-
-;;;; initialization from files
-
-(s/def :metabase.models.user.config-file-spec/first_name
-  string?)
-
-(s/def :metabase.models.user.config-file-spec/last_name
-  string?)
-
-(s/def :metabase.models.user.config-file-spec/password
-  string?)
-
-(s/def :metabase.models.user.config-file-spec/email
-  string?)
-
-(s/def ::config-file-spec
-  (s/keys :req-un [:metabase.models.user.config-file-spec/first_name
-                   :metabase.models.user.config-file-spec/last_name
-                   :metabase.models.user.config-file-spec/password
-                   :metabase.models.user.config-file-spec/email]))
-
-(defmethod config.file/section-spec :users
-  [_section]
-  (s/spec (s/* ::config-file-spec)))
-
-(defn- init-from-config-file-is-first-user?
-  "For [[init-from-config-file!]]: `true` if this the first User being created for this instance. If so, we will ALWAYS
-  create that User as a superuser, regardless of what is specified in the config file. (It doesn't make sense to
-  create the first User as anything other than a superuser)."
-  []
-  (zero? (db/count User)))
-
-(defn- init-from-config-file!
-  [user]
-  ;; TODO -- if this is the FIRST user, we should probably make them a superuser, right?
-  (if-let [existing-user-id (db/select-one-id User :email (:email user))]
-    (do
-      (log/info (u/colorize :blue (trs "Updating User with email {0}" (pr-str (:email user)))))
-      (db/update! User existing-user-id user))
-    ;; create a new user. If they are the first User, force them to be an admin.
-    (let [user (cond-> user
-                 (init-from-config-file-is-first-user?) (assoc :is_superuser true))]
-      (log/info (u/colorize :green (trs "Creating the first User for this instance. The first user is always created as an admin.")))
-      (log/info (u/colorize :green (trs "Creating new User {0} with email {1}"
-                                        (pr-str (str (:first_name user) \space (:last_name user)))
-                                        (pr-str (:email user)))))
-      (db/insert! User user))))
-
-(defmethod config.file/initialize-section! :users
-  [_section-name users]
-  (doseq [user users]
-    (init-from-config-file! user)))
diff --git a/test/metabase/email/messages_test.clj b/test/metabase/email/messages_test.clj
index 2f513fee0dd..41b5c45ac5d 100644
--- a/test/metabase/email/messages_test.clj
+++ b/test/metabase/email/messages_test.clj
@@ -1,10 +1,13 @@
 (ns metabase.email.messages-test
-  (:require [clojure.string :as str]
-            [clojure.test :refer :all]
-            [metabase.email-test :as et]
-            [metabase.email.messages :as messages]
-            [metabase.test.util :as tu])
-  (:import java.io.IOException))
+  (:require
+   [clojure.string :as str]
+   [clojure.test :refer :all]
+   [metabase.email-test :as et]
+   [metabase.email.messages :as messages]
+   [metabase.test :as mt]
+   [metabase.test.util :as tu])
+  (:import
+   (java.io IOException)))
 
 (deftest new-user-email
   (is (= [{:from    "notifications@metabase.com",
@@ -90,7 +93,7 @@
 
 (deftest render-pulse-email-test
   (testing "Email with few rows and columns can be rendered when tracing (#21166)"
-    (tu/with-log-level [metabase.email :trace]
+    (mt/with-log-level [metabase.email :trace]
       (let [result {:card   {:name "card-name"
                              :visualization_settings
                              {:table.column_formatting []}}
diff --git a/test/metabase/models/database_test.clj b/test/metabase/models/database_test.clj
index 8d0e390f6db..0dd088560db 100644
--- a/test/metabase/models/database_test.clj
+++ b/test/metabase/models/database_test.clj
@@ -4,11 +4,9 @@
    [clojure.string :as str]
    [clojure.test :refer :all]
    [metabase.api.common :as api]
-   [metabase.config.file :as config.file]
-   [metabase.db.connection :as mdb.connection]
    [metabase.driver :as driver]
    [metabase.driver.util :as driver.u]
-   [metabase.models :refer [Database Permissions Table]]
+   [metabase.models :refer [Database Permissions]]
    [metabase.models.database :as database]
    [metabase.models.interface :as mi]
    [metabase.models.permissions :as perms]
@@ -263,46 +261,3 @@
       (let [db (db/insert! Database (dissoc (mt/with-temp-defaults Database) :details))]
         (is (partial= {:details {}}
                       db))))))
-
-(deftest init-from-config-file-test
-  (let [db-type     (mdb.connection/db-type)
-        original-db (mt/with-driver db-type (mt/db))]
-    (try
-      (binding [config.file/*supported-versions* {:min 1, :max 1}
-                config.file/*config*             {:version 1
-                                                  :config  {:databases [{:name    "init-from-config-file-test/test-data"
-                                                                         :engine  (name db-type)
-                                                                         :details (:details original-db)}]}}]
-        (testing "Create a Database if it does not already exist"
-          (is (= :ok
-                 (config.file/initialize!)))
-          (let [db (db/select-one Database :name "init-from-config-file-test/test-data")]
-            (is (partial= {:engine db-type}
-                          db))
-            (is (= 1
-                   (db/count Database :name "init-from-config-file-test/test-data")))
-            (testing "do not duplicate if Database already exists"
-              (is (= :ok
-                     (config.file/initialize!)))
-              (is (= 1
-                     (db/count Database :name "init-from-config-file-test/test-data")))
-              (is (partial= {:engine db-type}
-                            (db/select-one Database :name "init-from-config-file-test/test-data"))))
-            (testing "Database should have been synced"
-              (is (= (db/count Table :db_id (u/the-id original-db))
-                     (db/count Table :db_id (u/the-id db))))))))
-      (finally
-        (db/delete! Database :name "init-from-config-file-test/test-data")))))
-
-(deftest ^:parallel init-from-config-file-connection-validation-test
-  (testing "Validate connection details when creating a Database from a config file, and error if they are invalid"
-    (binding [config.file/*supported-versions* {:min 1, :max 1}
-              config.file/*config*             {:version 1
-                                                :config  {:databases [{:name    "inist-from-config-file-test/test-data-in-memory"
-                                                                       :engine  "h2"
-                                                                       :details {:db "mem:some-in-memory-db"}}]}}]
-      (testing "Create a Database if it does not already exist"
-        (is (thrown-with-msg?
-             clojure.lang.ExceptionInfo
-             #"Database cannot be found\."
-             (config.file/initialize!)))))))
diff --git a/test/metabase/models/user_test.clj b/test/metabase/models/user_test.clj
index 3ed3494a05f..b2eb0b6f4e0 100644
--- a/test/metabase/models/user_test.clj
+++ b/test/metabase/models/user_test.clj
@@ -3,7 +3,6 @@
    [clojure.set :as set]
    [clojure.string :as str]
    [clojure.test :refer :all]
-   [metabase.config.file :as config.file]
    [metabase.http-client :as client]
    [metabase.models
     :refer [Collection
@@ -477,132 +476,3 @@
       (is (= "e8d63472"
              (serdes.hash/raw-hash ["fred@flintston.es"])
              (serdes.hash/identity-hash user))))))
-
-(deftest init-from-config-file-test
-  (try
-    (binding [config.file/*supported-versions* {:min 1, :max 1}
-              config.file/*config*             {:version 1
-                                                :config  {:users [{:first_name "Cam"
-                                                                   :last_name  "Era"
-                                                                   :email      "cam+config-file-test@metabase.com"
-                                                                   :password   "2cans"}]}}]
-      (testing "Create a User if it does not already exist"
-        (is (= :ok
-               (config.file/initialize!)))
-        (is (partial= {:first_name "Cam"
-                       :last_name  "Era"
-                       :email      "cam+config-file-test@metabase.com"}
-                      (db/select-one User :email "cam+config-file-test@metabase.com")))
-        (is (= 1
-               (db/count User :email "cam+config-file-test@metabase.com"))))
-      (testing "upsert if User already exists"
-        (binding [config.file/*config* {:version 1
-                                        :config  {:users [{:first_name "Cam"
-                                                           :last_name  "Saul"
-                                                           :email      "cam+config-file-test@metabase.com"
-                                                           :password   "2cans"}]}}]
-          (is (= :ok
-                 (config.file/initialize!)))
-          (is (= 1
-                 (db/count User :email "cam+config-file-test@metabase.com")))
-          (is (partial= {:first_name "Cam"
-                         :last_name  "Saul"
-                         :email      "cam+config-file-test@metabase.com"}
-                        (db/select-one User :email "cam+config-file-test@metabase.com"))))))
-    (finally
-      (db/delete! User :email "cam+config-file-test@metabase.com"))))
-
-(deftest init-from-config-file-force-admin-for-first-user-test
-  (testing "If this is the first user being created, always make the user a superuser regardless of what is specified"
-    (try
-      (binding [config.file/*supported-versions* {:min 1, :max 1}]
-        (testing "Create the first User"
-          (binding [config.file/*config* {:version 1
-                                          :config  {:users [{:first_name   "Cam"
-                                                             :last_name    "Era"
-                                                             :email        "cam+config-file-admin-test@metabase.com"
-                                                             :password     "2cans"
-                                                             :is_superuser false}]}}]
-            (with-redefs [user/init-from-config-file-is-first-user? (constantly true)]
-              (is (= :ok
-                     (config.file/initialize!)))
-              (is (partial= {:first_name   "Cam"
-                             :last_name    "Era"
-                             :email        "cam+config-file-admin-test@metabase.com"
-                             :is_superuser true}
-                            (db/select-one User :email "cam+config-file-admin-test@metabase.com")))
-              (is (= 1
-                     (db/count User :email "cam+config-file-admin-test@metabase.com"))))))
-        (testing "Create the another User, DO NOT force them to be an admin"
-          (binding [config.file/*config* {:version 1
-                                          :config  {:users [{:first_name   "Cam"
-                                                             :last_name    "Saul"
-                                                             :email        "cam+config-file-admin-test-2@metabase.com"
-                                                             :password     "2cans"
-                                                             :is_superuser false}]}}]
-            (is (= :ok
-                   (config.file/initialize!)))
-            (is (partial= {:first_name   "Cam"
-                           :last_name    "Saul"
-                           :email        "cam+config-file-admin-test-2@metabase.com"
-                           :is_superuser false}
-                          (db/select-one User :email "cam+config-file-admin-test-2@metabase.com")))
-            (is (= 1
-                   (db/count User :email "cam+config-file-admin-test-2@metabase.com"))))))
-      (finally (db/delete! User :email [:in #{"cam+config-file-admin-test@metabase.com"
-                                              "cam+config-file-admin-test-2@metabase.com"}])))))
-
-(deftest init-from-config-file-env-var-for-password-test
-  (testing "Ensure that we can set User password using {{env ...}} templates"
-    (try
-      (binding [config.file/*supported-versions* {:min 1, :max 1}
-                config.file/*config*             {:version 1
-                                                  :config  {:users [{:first_name "Cam"
-                                                                     :last_name  "Era"
-                                                                     :email      "cam+config-file-password-test@metabase.com"
-                                                                     :password   "{{env USER_PASSWORD}}"}]}}
-                config.file/*env*                (assoc @#'config.file/*env* :user-password "1234cans")]
-        (testing "Create a User if it does not already exist"
-          (is (= :ok
-                 (config.file/initialize!)))
-          (let [user (db/select-one [User :first_name :last_name :email :password_salt :password]
-                       :email "cam+config-file-password-test@metabase.com")]
-            (is (partial= {:first_name "Cam"
-                           :last_name  "Era"
-                           :email      "cam+config-file-password-test@metabase.com"}
-                          user))
-            (is (u.password/verify-password "1234cans" (:password_salt user) (:password user))))))
-      (finally
-        (db/delete! User :email "cam+config-file-password-test@metabase.com")))))
-
-(deftest ^:parallel init-from-config-file-validation-test
-  (binding [config.file/*supported-versions* {:min 1, :max 1}]
-    (are [user error-pattern] (thrown-with-msg?
-                               clojure.lang.ExceptionInfo
-                               error-pattern
-                               (binding [config.file/*config* {:version 1
-                                                               :config  {:users [user]}}]
-                                 (#'config.file/config)))
-      ;; missing email
-      {:first_name "Cam"
-       :last_name  "Era"
-       :password   "2cans"}
-      (re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :email)"))
-
-      ;; missing first name
-      {:last_name  "Era"
-       :email      "cam+config-file-admin-test@metabase.com"
-       :password   "2cans"}
-      (re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :first_name)"))
-
-      ;; missing last name
-      {:first_name "Cam"
-       :email      "cam+config-file-admin-test@metabase.com"
-       :password   "2cans"}
-      (re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :last_name)"))
-
-      ;; missing password
-      {:first_name "Cam"
-       :last_name  "Era"
-       :email      "cam+config-file-test@metabase.com"}
-      (re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :password)")))))
diff --git a/test/metabase/public_settings/premium_features_test.clj b/test/metabase/public_settings/premium_features_test.clj
index 18148289d9d..6bb1a28178c 100644
--- a/test/metabase/public_settings/premium_features_test.clj
+++ b/test/metabase/public_settings/premium_features_test.clj
@@ -1,16 +1,18 @@
 (ns metabase.public-settings.premium-features-test
-  (:require [cheshire.core :as json]
-            [clj-http.client :as http]
-            [clj-http.fake :as http-fake]
-            [clojure.test :refer :all]
-            [metabase.config :as config]
-            [metabase.models.user :refer [User]]
-            [metabase.public-settings :as public-settings]
-            [metabase.public-settings.premium-features :as premium-features :refer [defenterprise defenterprise-schema]]
-            [metabase.test :as mt]
-            [metabase.test.util :as tu]
-            [schema.core :as s]
-            [toucan.util.test :as tt]))
+  (:require
+   [cheshire.core :as json]
+   [clj-http.client :as http]
+   [clj-http.fake :as http-fake]
+   [clojure.test :refer :all]
+   [metabase.config :as config]
+   [metabase.models.user :refer [User]]
+   [metabase.public-settings :as public-settings]
+   [metabase.public-settings.premium-features
+    :as premium-features
+    :refer [defenterprise defenterprise-schema]]
+   [metabase.test :as mt]
+   [schema.core :as s]
+   [toucan.util.test :as tt]))
 
 (defn do-with-premium-features [features f]
   (let [features (set (map name features))]
@@ -99,7 +101,7 @@
           (is (contains? (set (:features result)) "test")))))))
 
 (deftest not-found-test
-  (tu/with-log-level :fatal
+  (mt/with-log-level :fatal
     ;; `partial=` here in case the Cloud API starts including extra keys... this is a "dangerous" test since changes
     ;; upstream in Cloud could break this. We probably want to catch that stuff anyway tho in tests rather than waiting
     ;; for bug reports to come in
diff --git a/test/metabase/test/util.clj b/test/metabase/test/util.clj
index 654a43600c6..fa30382b692 100644
--- a/test/metabase/test/util.clj
+++ b/test/metabase/test/util.clj
@@ -1,40 +1,63 @@
 (ns metabase.test.util
   "Helper functions and macros for writing unit tests."
-  (:require [cheshire.core :as json]
-            [clojure.java.io :as io]
-            [clojure.set :as set]
-            [clojure.string :as str]
-            [clojure.test :refer :all]
-            [clojure.walk :as walk]
-            [clojurewerkz.quartzite.scheduler :as qs]
-            [colorize.core :as colorize]
-            [environ.core :as env]
-            [java-time :as t]
-            [metabase.models :refer [Card Collection Dashboard DashboardCardSeries Database Dimension Field FieldValues
-                                     LoginHistory Metric NativeQuerySnippet Permissions PermissionsGroup PermissionsGroupMembership
-                                     PersistedInfo Pulse PulseCard PulseChannel Revision Segment Setting
-                                     Table TaskHistory Timeline TimelineEvent User]]
-            [metabase.models.collection :as collection]
-            [metabase.models.interface :as mi]
-            [metabase.models.permissions :as perms]
-            [metabase.models.permissions-group :as perms-group]
-            [metabase.models.setting :as setting]
-            [metabase.models.setting.cache :as setting.cache]
-            [metabase.models.timeline :as timeline]
-            [metabase.plugins.classloader :as classloader]
-            [metabase.task :as task]
-            [metabase.test-runner.assert-exprs :as test-runner.assert-exprs]
-            [metabase.test-runner.parallel :as test-runner.parallel]
-            [metabase.test.data :as data]
-            [metabase.test.fixtures :as fixtures]
-            [metabase.test.initialize :as initialize]
-            [metabase.test.util.log :as tu.log]
-            [metabase.util :as u]
-            [metabase.util.files :as u.files]
-            [potemkin :as p]
-            [toucan.db :as db]
-            [toucan.models :as models]
-            [toucan.util.test :as tt])
+  (:require
+   [cheshire.core :as json]
+   [clojure.java.io :as io]
+   [clojure.set :as set]
+   [clojure.string :as str]
+   [clojure.test :refer :all]
+   [clojure.walk :as walk]
+   [clojurewerkz.quartzite.scheduler :as qs]
+   [colorize.core :as colorize]
+   [environ.core :as env]
+   [java-time :as t]
+   [metabase.models
+    :refer [Card
+            Collection
+            Dashboard
+            DashboardCardSeries
+            Database
+            Dimension
+            Field
+            FieldValues
+            LoginHistory
+            Metric
+            NativeQuerySnippet
+            Permissions
+            PermissionsGroup
+            PermissionsGroupMembership
+            PersistedInfo
+            Pulse
+            PulseCard
+            PulseChannel
+            Revision
+            Segment
+            Setting
+            Table
+            TaskHistory
+            Timeline
+            TimelineEvent
+            User]]
+   [metabase.models.collection :as collection]
+   [metabase.models.interface :as mi]
+   [metabase.models.permissions :as perms]
+   [metabase.models.permissions-group :as perms-group]
+   [metabase.models.setting :as setting]
+   [metabase.models.setting.cache :as setting.cache]
+   [metabase.models.timeline :as timeline]
+   [metabase.plugins.classloader :as classloader]
+   [metabase.task :as task]
+   [metabase.test-runner.assert-exprs :as test-runner.assert-exprs]
+   [metabase.test-runner.parallel :as test-runner.parallel]
+   [metabase.test.data :as data]
+   [metabase.test.fixtures :as fixtures]
+   [metabase.test.initialize :as initialize]
+   [metabase.test.util.log :as tu.log]
+   [metabase.util :as u]
+   [metabase.util.files :as u.files]
+   [toucan.db :as db]
+   [toucan.models :as models]
+   [toucan.util.test :as tt])
   (:import
    (java.io File FileInputStream)
    (java.net ServerSocket)
@@ -48,14 +71,6 @@
 
 (use-fixtures :once (fixtures/initialize :db))
 
-;; these are imported because these functions originally lived in this namespace, and some tests might still be
-;; referencing them from here. We can remove the imports once everyone is using `metabase.test` instead of using this
-;; namespace directly.
-(p/import-vars
- [tu.log
-  with-log-level
-  with-log-messages-for-level])
-
 (defn- random-uppercase-letter []
   (char (+ (int \A) (rand-int 26))))
 
diff --git a/test/metabase/util/encryption_test.clj b/test/metabase/util/encryption_test.clj
index c4163e9c3c5..2a557d0af25 100644
--- a/test/metabase/util/encryption_test.clj
+++ b/test/metabase/util/encryption_test.clj
@@ -1,11 +1,12 @@
 (ns metabase.util.encryption-test
   "Tests for encryption of Metabase DB details."
-  (:require [clojure.string :as str]
-            [clojure.test :refer :all]
-            [metabase.models.setting.cache :as setting.cache]
-            [metabase.test.initialize :as initialize]
-            [metabase.test.util :as tu]
-            [metabase.util.encryption :as encryption]))
+  (:require
+   [clojure.string :as str]
+   [clojure.test :refer :all]
+   [metabase.models.setting.cache :as setting.cache]
+   [metabase.test :as mt]
+   [metabase.test.initialize :as initialize]
+   [metabase.util.encryption :as encryption]))
 
 (defn do-with-secret-key [^String secret-key thunk]
   ;; flush the Setting cache so unencrypted values have to be fetched from the DB again
@@ -88,7 +89,7 @@
 
 (deftest no-errors-for-unencrypted-test
   (testing "Something obviously not encrypted should avoiding trying to decrypt it (and thus not log an error)"
-    (is (empty? (tu/with-log-messages-for-level :warn
+    (is (empty? (mt/with-log-messages-for-level :warn
                   (encryption/maybe-decrypt secret "abc"))))))
 
 (def ^:private fake-ciphertext
@@ -101,10 +102,10 @@
   (testing (str "Something that is not encrypted, but might be (is the correct shape etc) should attempt to be "
                 "decrypted. If unable to decrypt it, log a warning.")
     (is (includes-encryption-warning?
-         (tu/with-log-messages-for-level :warn
+         (mt/with-log-messages-for-level :warn
            (encryption/maybe-decrypt secret fake-ciphertext))))
     (is (includes-encryption-warning?
-         (tu/with-log-messages-for-level :warn
+         (mt/with-log-messages-for-level :warn
            (encryption/maybe-decrypt secret-2 (encryption/encrypt secret "WOW")))))))
 
 (deftest ^:parallel possibly-encrypted-test
-- 
GitLab