2019-06-03 22:44:12 +00:00
import json
import os
from . import exceptions
from . import env
from . import fmt
from . import plugins
from . import serialize
from . import utils
2019-06-05 13:43:51 +00:00
def update ( root ) :
"""
Load and save the configuration .
"""
2019-06-03 22:44:12 +00:00
config , defaults = load_all ( root )
2019-06-05 13:43:51 +00:00
save ( root , config )
2019-06-03 22:44:12 +00:00
merge ( config , defaults )
2019-06-05 13:43:51 +00:00
return config
2019-06-03 22:44:12 +00:00
def load ( root ) :
"""
2019-06-05 13:43:51 +00:00
Load full configuration . This will raise an exception if there is no current
configuration in the project root .
2019-06-03 22:44:12 +00:00
"""
2019-06-05 13:43:51 +00:00
check_existing_config ( root )
config , defaults = load_all ( root )
2019-06-03 22:44:12 +00:00
merge ( config , defaults )
return config
2019-06-05 13:43:51 +00:00
def load_all ( root ) :
2019-06-03 22:44:12 +00:00
"""
2019-06-05 13:43:51 +00:00
Return :
current ( dict ) : params currently saved in config . yml
defaults ( dict ) : default values of params which might be missing from the
current config
2019-06-03 22:44:12 +00:00
"""
2019-06-05 13:43:51 +00:00
defaults = load_defaults ( )
current = load_current ( root , defaults )
return current , defaults
2019-06-03 22:44:12 +00:00
2019-06-05 13:43:51 +00:00
def merge ( config , defaults , force = False ) :
2019-06-03 22:44:12 +00:00
"""
2019-06-05 13:43:51 +00:00
Merge default values with user configuration and perform rendering of " {{ ...}} "
values .
2019-06-03 22:44:12 +00:00
"""
2019-06-05 13:43:51 +00:00
for key , value in defaults . items ( ) :
if force or key not in config :
config [ key ] = env . render_unknown ( config , value )
2019-06-03 22:44:12 +00:00
def load_defaults ( ) :
return serialize . load ( env . read ( " config.yml " ) )
def load_current ( root , defaults ) :
"""
2019-06-05 13:43:51 +00:00
Load the configuration currently stored on disk .
Note : this modifies the defaults with the plugin default values .
2019-06-03 22:44:12 +00:00
"""
convert_json2yml ( root )
config = load_user ( root )
load_env ( config , defaults )
load_required ( config , defaults )
load_plugins ( config , defaults )
return config
def load_user ( root ) :
path = config_path ( root )
if not os . path . exists ( path ) :
return { }
with open ( path ) as fi :
config = serialize . load ( fi . read ( ) )
upgrade_obsolete ( config )
return config
def load_env ( config , defaults ) :
for k in defaults . keys ( ) :
env_var = " TUTOR_ " + k
if env_var in os . environ :
config [ k ] = serialize . parse_value ( os . environ [ env_var ] )
def load_required ( config , defaults ) :
"""
2019-07-03 14:09:33 +00:00
All these keys must be present in the user ' s config.yml. This includes all values
that are generated once and must be kept after that , such as passwords .
2019-06-03 22:44:12 +00:00
"""
for key in [
" SECRET_KEY " ,
" MYSQL_ROOT_PASSWORD " ,
" OPENEDX_MYSQL_PASSWORD " ,
" ANDROID_OAUTH2_SECRET " ,
" ID " ,
] :
if key not in config :
2019-06-05 13:43:51 +00:00
config [ key ] = env . render_unknown ( config , defaults [ key ] )
2019-06-03 22:44:12 +00:00
2019-06-05 17:57:30 +00:00
2019-06-03 22:44:12 +00:00
def load_plugins ( config , defaults ) :
2019-06-05 13:43:51 +00:00
"""
Add , override and set new defaults from plugins .
"""
for plugin_name , plugin in plugins . iter_enabled ( config ) :
plugin_prefix = plugin_name . upper ( ) + " _ "
2019-06-05 18:07:41 +00:00
plugin_config = plugins . get_callable_attr ( plugin , " config " , { } )
2019-06-05 13:43:51 +00:00
# Add new config key/values
for key , value in plugin_config . get ( " add " , { } ) . items ( ) :
2019-07-03 14:09:33 +00:00
new_key = plugin_prefix + key
if new_key not in config :
config [ new_key ] = env . render_unknown ( config , value )
2019-06-05 13:43:51 +00:00
# Set existing config key/values: here, we do not override existing values
for key , value in plugin_config . get ( " set " , { } ) . items ( ) :
if key not in config :
config [ key ] = env . render_unknown ( config , value )
# Create new defaults
for key , value in plugin_config . get ( " defaults " , { } ) . items ( ) :
defaults [ plugin_prefix + key ] = value
2019-06-03 22:44:12 +00:00
def upgrade_obsolete ( config ) :
# Openedx-specific mysql passwords
if " MYSQL_PASSWORD " in config :
config [ " MYSQL_ROOT_PASSWORD " ] = config [ " MYSQL_PASSWORD " ]
config [ " OPENEDX_MYSQL_PASSWORD " ] = config [ " MYSQL_PASSWORD " ]
config . pop ( " MYSQL_PASSWORD " )
if " MYSQL_DATABASE " in config :
config [ " OPENEDX_MYSQL_DATABASE " ] = config . pop ( " MYSQL_DATABASE " )
if " MYSQL_USERNAME " in config :
config [ " OPENEDX_MYSQL_USERNAME " ] = config . pop ( " MYSQL_USERNAME " )
2019-07-03 14:09:33 +00:00
if " ACTIVATE_NOTES " in config :
if config [ " ACTIVATE_NOTES " ] :
plugins . enable ( config , " notes " )
config . pop ( " ACTIVATE_NOTES " )
2019-07-02 20:16:44 +00:00
if " ACTIVATE_XQUEUE " in config :
if config [ " ACTIVATE_XQUEUE " ] :
plugins . enable ( config , " xqueue " )
config . pop ( " ACTIVATE_XQUEUE " )
2019-06-03 22:44:12 +00:00
def convert_json2yml ( root ) :
2019-06-05 13:43:51 +00:00
"""
Older versions of tutor used to have json config files .
"""
2019-06-03 22:44:12 +00:00
json_path = os . path . join ( root , " config.json " )
if not os . path . exists ( json_path ) :
return
if os . path . exists ( config_path ( root ) ) :
raise exceptions . TutorError (
" Both config.json and config.yml exist in {} : only one of these files must exist to continue " . format (
root
)
)
with open ( json_path ) as fi :
config = json . load ( fi )
2019-06-05 13:43:51 +00:00
save ( root , config )
2019-06-03 22:44:12 +00:00
os . remove ( json_path )
fmt . echo_info (
" File config.json detected in {} and converted to config.yml " . format ( root )
)
2019-06-05 13:43:51 +00:00
def save ( root , config ) :
2019-06-03 22:44:12 +00:00
path = config_path ( root )
utils . ensure_file_directory_exists ( path )
with open ( path , " w " ) as of :
serialize . dump ( config , of )
fmt . echo_info ( " Configuration saved to {} " . format ( path ) )
2019-06-05 13:43:51 +00:00
def check_existing_config ( root ) :
"""
Check there is a configuration on disk and the current environment is up - to - date .
"""
if not os . path . exists ( config_path ( root ) ) :
raise exceptions . TutorError (
2019-06-05 17:45:22 +00:00
" Project root does not exist. Make sure to generate the initial configuration with `tutor config save --interactive` or `tutor config quickstart` prior to running other commands. "
2019-06-05 13:43:51 +00:00
)
env . check_is_up_to_date ( root )
2019-06-03 22:44:12 +00:00
def config_path ( root ) :
return os . path . join ( root , " config.yml " )