cli.py (3774B)
1 ## 2 # This file is part of GNU Taler 3 # (C) 2017,2021 Taler Systems S.A. 4 # 5 # GNU Taler is free software; you can redistribute it and/or 6 # modify it under the terms of the GNU Affero General Public 7 # License as published by the Free Software Foundation; either 8 # version 3, or (at your option) any later version. 9 # 10 # GNU Taler is distributed in the hope that it will be useful, 11 # but WITHOUT ANY WARRANTY; without even the implied warranty 12 # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 # See the GNU General Public License for more details. 14 # 15 # You should have received a copy of the GNU General Public 16 # License along with GNU Taler; see the file COPYING. If not, 17 # see <http://www.gnu.org/licenses/> 18 # 19 # @author Florian Dold 20 # @file Standalone script to run the blog. 21 22 import click 23 import logging 24 import sys 25 import os 26 from .util.talerconfig import TalerConfig 27 28 import importlib 29 import gunicorn.app.base 30 31 logger = logging.getLogger(__name__) 32 33 def bindspec(config, which_shop, port): 34 confsection = f"frontend-demo-{which_shop}" 35 36 # Takes precedence. 37 if port: 38 http_serve = "tcp" 39 else: 40 http_serve = ( 41 config[confsection]["http_serve"] 42 .value_string(required=False, default="tcp") 43 .lower() 44 ) 45 46 if http_serve == "tcp": 47 port_launch = ( 48 config[confsection]["http_port"].value_int(required=False) 49 if not port 50 else port 51 ) 52 if not port_launch: 53 print("Port number wasn't found in config and in arguments.", file=sys.stderr) 54 exit(1) 55 return dict(bind=f"0.0.0.0:{port_launch}") 56 57 if http_serve == "unix": 58 path = config[confsection]["http_unixpath"].value_filename(required=True) 59 mode = config[confsection]["http_unixpath_mode"].value_filename(required=True) 60 return dict(bind=f"unix:{path}", umask=mode) 61 62 63 def number_of_workers(config, which_shop): 64 confsection = f"frontend-demo-{which_shop}" 65 nw = config[confsection]["num_workers"].value_int(required=False) 66 if nw is None: 67 # Be conservative with number of workers 68 return 2 69 else: 70 return nw 71 72 73 class StandaloneApplication(gunicorn.app.base.BaseApplication): 74 75 def __init__(self, app, options=None): 76 self.options = options or {} 77 self.application = app 78 super().__init__() 79 80 def load_config(self): 81 config = { 82 key: value 83 for key, value in self.options.items() 84 if key in self.cfg.settings and value is not None 85 } 86 for key, value in config.items(): 87 self.cfg.set(key.lower(), value) 88 89 def load(self): 90 return self.application 91 92 93 @click.command("Global shop launcher") 94 @click.option("-c", "--config", 'config_filename', help="Configuration file", required=False) 95 @click.option( 96 "--http-port", 97 help="HTTP port to serve (if not given, serving comes from config)", 98 required=False, 99 type=int, 100 ) 101 @click.argument("which-shop") 102 def demos(config_filename, http_port, which_shop): 103 """WHICH_SHOP is one of: blog, donations, or landing.""" 104 105 logging.basicConfig(level=logging.INFO) 106 107 if which_shop not in ["blog", "donations", "landing"]: 108 print("Please use a valid shop name: blog, donations, landing.") 109 sys.exit(1) 110 config = TalerConfig.from_file(config_filename) 111 options = { 112 "workers": number_of_workers(config, which_shop), 113 **bindspec(config, which_shop, http_port), 114 } 115 mod = f"talermerchantdemos.{which_shop}" 116 os.environ["TALER_CONFIG_FILENAME"] = config_filename or "" 117 app = importlib.import_module(mod).app 118 StandaloneApplication(app, options).run() 119 120 121 def run(): 122 demos() 123 124 if __name__ == "__main__": 125 run()