#!/usr/bin/env python3 """ This script makes sure that the merchant backend instances used by the test/demo environment are created. We assume that the merchant backend is running, and that the "~/activate" file has been sourced to provide the right environment variables. """ import requests from os import environ, system from sys import exit from urllib.parse import urljoin from subprocess import Popen from time import sleep import psutil from getpass import getuser def expect_env(name): val = environ.get(name) if not val: print(f"{name} not defined. Please source the ~/activate file.") exit(1) return val def wait_merchant_up(): # Check it started correctly and it is ready to serve requests. checks = 10 url = urljoin(MERCHANT_BACKEND_BASE_URL, "/config") print("Check URL: {}".format(url)) while checks > 0: try: resp = requests.get(url, timeout=5) except Exception: print("Merchant unreachable") sleep(1) checks -= 1 continue if resp.status_code != 200: sleep(1) checks -= 1 continue # Ready. print("Merchant is up and running") return True if checks == 0: print("Merchant is not correctly serving requests.") return False MERCHANT_BACKEND_BASE_URL = expect_env("TALER_ENV_MERCHANT_BACKEND") TALER_ENV_NAME = expect_env("TALER_ENV_NAME") TALER_CONFIG_CURRENCY = expect_env("TALER_CONFIG_CURRENCY") TALER_ENV_FRONTENDS_APITOKEN = expect_env("TALER_ENV_FRONTENDS_APITOKEN") authorization_header = {"Authorization": f"Bearer {TALER_ENV_FRONTENDS_APITOKEN}"} def ensure_instance(instance_id, name, payto_uris, auth): resp = requests.get( urljoin(MERCHANT_BACKEND_BASE_URL, f"management/instances/{instance_id}"), headers = authorization_header ) # Instance exists, we PATCH the auth just in case it changed. if resp.status_code == 200: if instance_id != "Tutorial": print(f"Patching (auth of) instance '{instance_id}'") patch_resp = requests.post( urljoin(MERCHANT_BACKEND_BASE_URL, f"management/instances/{instance_id}/auth"), json=auth, headers = authorization_header ) if patch_resp.status_code < 200 or patch_resp.status_code >= 300: print(f"Failed to update auth of '{instance_id}', backend responds: {patch_resp.status_code}/{patch_resp.text}") exit(1) return print(f"Instance '{instance_id}' not found, trying to create it.") req = dict( id=instance_id, name=name, payto_uris=payto_uris, address=dict(), jurisdiction=dict(), default_max_wire_fee=f"{TALER_CONFIG_CURRENCY}:1", default_wire_fee_amortization=3, default_max_deposit_fee=f"{TALER_CONFIG_CURRENCY}:1", default_wire_transfer_delay=dict(d_ms="forever"), default_pay_delay=dict(d_ms="forever"), auth=auth, ) create_resp = requests.post( urljoin(MERCHANT_BACKEND_BASE_URL, "management/instances"), json=req, headers = authorization_header ) if create_resp.status_code < 200 or create_resp.status_code >= 300: print(f"Could not create instance '{instance_id}', backend responds: {create_resp.status_code}/{create_resp.text}") exit(1) def is_merchant_running(): for proc in psutil.process_iter(): if proc.name() == "taler-merchant-httpd" and proc.username() == getuser(): return True return False def ensure_default_instance(): # Assumed is managed by ARM merchant_was_running = is_merchant_running() if merchant_was_running: print("Found running merchant, assuming is managed by ARM. Terminating it") system("taler-deployment-arm -k taler-merchant") checks = 10 while checks > 0: if is_merchant_running(): sleep(1) checks -= 1 continue break if checks == 0: print("Could not stop the running merchant.") exit(1) print("Successfully terminating the merchant.") # ARM is _not_ running the merchant at this point. env_with_token = environ.copy() env_with_token["TALER_MERCHANT_TOKEN"] = TALER_ENV_FRONTENDS_APITOKEN print("Starting the merchant outside ARM, passing the token into the environment.") # Start the merchant natively. merchant = Popen(["taler-merchant-httpd"], env=env_with_token) if not wait_merchant_up(): merchant.terminate() merchant.wait() exit(1) print("Merchant started successfully, creating the default instance now.") ensure_instance( "default", "default", payto_uris=[f"payto://x-taler-bank/bank.{TALER_ENV_NAME}.taler.net/Taler"], auth=dict(method="token", token=TALER_ENV_FRONTENDS_APITOKEN) ) # Native process can be terminated now. merchant.terminate() merchant.wait() print("Merchant terminated, restarting it via ARM now.") # Restarting the official ARM merchant. if merchant_was_running: system("taler-deployment-arm -i taler-merchant") ensure_default_instance() # Needs to wait here since the merchant got lastly restarted via ARM, # in the previous operation. if not wait_merchant_up(): system("taler-deployment-arm -k taler-merchant") exit(1) ensure_instance( "blog", name="Blog", payto_uris=[f"payto://x-taler-bank/bank.{TALER_ENV_NAME}.taler.net/blog"], auth=dict(method="token", token=TALER_ENV_FRONTENDS_APITOKEN), ) ensure_instance( "donations", name="Donations", payto_uris=[f"payto://x-taler-bank/bank.{TALER_ENV_NAME}.taler.net/donations"], auth=dict(method="token", token=TALER_ENV_FRONTENDS_APITOKEN), ) ensure_instance( "survey", name="Survey", payto_uris=[f"payto://x-taler-bank/bank.{TALER_ENV_NAME}.taler.net/survey"], auth=dict(method="token", token=TALER_ENV_FRONTENDS_APITOKEN), ) ensure_instance( "pos", name="PoS", payto_uris=[f"payto://x-taler-bank/bank.{TALER_ENV_NAME}.taler.net/pos"], auth=dict(method="token", token=TALER_ENV_FRONTENDS_APITOKEN), ) ensure_instance( "GNUnet", name="GNUnet", payto_uris=[f"payto://x-taler-bank/bank.{TALER_ENV_NAME}.taler.net/GNUnet"], auth=dict(method="token", token=TALER_ENV_FRONTENDS_APITOKEN), ) # This instance relate to both the donation receiver and the sync service. ensure_instance( "Taler", name="Taler", payto_uris=[f"payto://x-taler-bank/bank.{TALER_ENV_NAME}.taler.net/Taler"], auth=dict(method="token", token=TALER_ENV_FRONTENDS_APITOKEN), ) ensure_instance( "Tor", name="Tor", payto_uris=[f"payto://x-taler-bank/bank.{TALER_ENV_NAME}.taler.net/Tor"], auth=dict(method="token", token=TALER_ENV_FRONTENDS_APITOKEN), ) ensure_instance( "anastasis", name="Tor", payto_uris=[f"payto://x-taler-bank/bank.{TALER_ENV_NAME}.taler.net/anastasis"], auth=dict(method="token", token=TALER_ENV_FRONTENDS_APITOKEN), ) # Note: this instance has a fixed secret-token, so as to allow anyone to easily # run their tutorial. ensure_instance( "Tutorial", name="Tutorial", payto_uris=[f"payto://x-taler-bank/bank.{TALER_ENV_NAME}.taler.net/Tutorial"], auth=dict(method="token", token="secret-token:sandbox") )