type: install
jpsVersion: '1.7.2'
name: Multi-Region WordPress Cluster v1 (Alpha)
id: wordpress-cluster-multiregions
categories:
  - apps/clusters
  - apps/content-management
description: 
  text: Highly-available and reliable WordPress cluster setup with automatic multi-region distribution. The package creates interconnected WordPress sub clusters across several regions ensuring the highest uptime and lowest latency.
  short: Enterprise-grade WordPress Cluster with automatic distribution across several regions.
logo: https://raw.githubusercontent.com/jelastic-jps/wordpress-multiregions/master/images/82dark-back-01.svg
baseUrl: https://raw.githubusercontent.com/jelastic-jps/wordpress-multiregions/master

onBeforeInit: /scripts/settings.js?_r=${fn.random}

settings:
  fields:
    - caption: Database Topology
      type: list
      name: db_async_topology
      default: async
      required: true
      width: 443
      tooltip:
      tipParams: 
        maxWidth: 610
        dismissDelay: 600000
        hideOnOver: false
      values:        
          - value: async
            caption: Async Primary/Replica Distribution 
            tooltip: <h2>Primary Replication with Extra Replicas</h2> 
                     Pre-configured asynchronous replication with two interconnected<br> primary databases and one secondary replica.<br>
                     This topology is recommended when <b>Latency</b> between regions is more than <b>20ms</b>.<br>
                     <ul>
                        <li>Primary node at Region 1 performs writes and reads operations<br>
                        <li>Primary node at Region 2 by default performs reads operations but starts handling writes if initial primary database becomes unavailable<br>
                        <li>Replica node at Region 3 is used for reads operations only<br>
                     </ul>
                       <img width='587' height='480' src='https://raw.githubusercontent.com/jelastic-jps/wordpress-multiregions/master/images/wp-geo-mm-white-1-bl.svg?sanitize=true'> 
          - value: sync
            caption: Sync Galera Distribution
            tooltip: <h2>Galera Cluster</h2>
                     All servers can accept updates even if being issued concurrently.<br>
                     This topology is recommended when <b>Latency</b> between regions is less than <b>20ms</b><br>
                     <ul>
                        <li>Region 1 includes 3 primary database nodes<br>
                        <li>Region 2 and 3 each includes 2 primary database nodes<br>
                        <li>All Galera Cluster nodes perform reads and writes operation<br>
                     </ul>
                      <img width='580' height='400' src='https://raw.githubusercontent.com/jelastic-jps/wordpress-multiregions/master/images/wp-geo-galera-white-1-bl.svg?sanitize=true'>
                      
      showIf:
        async:
          - caption: Regions
            type: regionlist
            name: regions
            disableInactive: true
            selectFirstAvailable: false
            multiSelect: true
            required: true
            min: 2
            max: 5
            filter:
              isActive: true
              type: ["vz7"]
            tooltip: Select the destination regions. The first selected region will host the primary cluster, so this region should have the highest performance.
        sync:
          - caption: Regions
            type: regionlist
            name: regions
            disableInactive: true
            selectFirstAvailable: false
            multiSelect: true
            required: true
            min: 3
            max: 5
            filter:
              isActive: true
              type: ["vz7"]
            tooltip: Select the destination regions. The first selected region will host the primary cluster, so this region should have the highest performance.    

    - caption: Environment
      type: envname
      name: envName
      dependsOn: region
      randomName: true
      showFullDomain: false
      required: true

    - caption: Scaling Strategy
      type: list
      name: loadGrowth
      default: slow
      required: true
      width: 225
      tooltip: | 
        Configure auto-scaling triggers, i.e. how fast the system responds to load spikes by adding or removing nodes.
        <p>Read more about <a href="https://docs.jelastic.com/automatic-horizontal-scaling">Automatic Horizontal Scaling</a></p>
      values:        
          - value: slow
            caption: Low Load
            tooltip: <h2>Low load scaling strategy</h2>add 1 new node when CPU > 70% <p>remove when CPU < 20%</p>
          - value: medium
            caption: Medium Load
            tooltip: <h3>Medium load scaling strategy</h3>add 1 new node when CPU > 50% <p>remove when CPU < 20%</p>
          - value: fast
            caption: High Load
            tooltip: <h3>High load scaling strategy</h3>add 2 new nodes when CPU > 30% <p>remove when CPU < 10%</p>

    - caption: Advanced Features
      type: displayfield
      name: displayfield
      markup:
 
    - caption: WordPress Brute Force Attack Protection
      type: checkbox
      name: wp_protect
      value: true
      disabled: false
      tooltip: "Secure WordPress Admin Panel with <a href='https://www.litespeedtech.com/support/wiki/doku.php/litespeed_wiki:config:wordpress-protection'>LiteSpeed Brute Force Protection</a> that limits failed login attempts. Default action is <b>Throttle</b> and number of allowed attempts is <b>100</b>."

    - caption: Web Application Firewall
      type: checkbox
      name: waf
      value: true
      disabled: false
      tooltip: "Protect web sites with <a href='https://www.litespeedtech.com/support/wiki/doku.php/litespeed_wiki:waf'>LiteSpeed built-in WAF</a> based on Free ModSecurity Rules from Comodo."

    - caption: Install Let's Encrypt SSL with Auto-Renewal
      type: checkbox
      name: le_addon
      value: true
      disabled: false
      tooltip: "Advanced integration with Let's Encrypt certificate authority that simplifies and automates the process of issuing, configuring and renewing trusted SSL certificates."

    - caption: Install Lightning-Fast Premium CDN with 160+ PoPs
      type: checkbox
      name: cdn_addon
      value: false
      hidden: true
      disabled: false
      tooltip: "Jelastic CDN is an HTTP/3 premium content delivery network of 160+ Super PoPs (points of presence) with bandwidth capacity up to 115 Tbps, advanced caching and acceleration strategies based on best-in-class IP Anycast technology."

    - caption: Install WordPress Multisite Network
      type: checkbox
      name: mu_addon
      value: false
      disabled: false
      tooltip: "Multisite is a type of WordPress installation that allows you to create and manage a network of multiple websites from a single WordPress dashboard. This lets you easily make changes and keep all of your websites updated from one place."

    - type: displayfield
      hideLabel: true
      hidden: true
      name: bl_count   
      value: 1
      markup:
mixins:
 - https://raw.githubusercontent.com/jelastic-jps/wordpress-cluster/master/configs/vers.yaml

globals:
  wp_cluster_url: https://raw.githubusercontent.com/jelastic-jps/wordpress-cluster/v3.0.0
  db_cluster_url: https://raw.githubusercontent.com/jelastic-jps/mysql-cluster/v3.0.0
  galera_server_id: ${fn.random}
  db_user: jelastic-${fn.random}
  db_pass: ${fn.password(10)}
  repl_user: replica-${fn.random}
  repl_pass: ${fn.password(10)}
  monitor_user: monitor-${fn.random}
  monitor_pass: ${fn.password(10)}
  protocol: http
  wp_admin_pass: ${fn.password(10)}
  ls_admin_pass: ${fn.password(10)}
  success: default
  email: default

onInstall:
  - if ('${settings.db_async_topology}' == 'async'):
      setGlobals:
        db_host: proxy
        db_async_topology: true
  - else:
      setGlobals:
        db_host: sqldb
        db_async_topology: false      
  - initGlobals
  - setGlobals:
      domain: ${settings.envName}-1.${globals.domain-1}
  - createEnvs    
  - getEnvNodes
  - installEnvsManager
  
  - if ('${globals.db_async_topology}' == 'true'):  
    - install:
      - jps: ${baseUrl}/scripts/asyncReplicationManager.jps
        envName: ${settings.envName}-1
        settings:
          db_user: ${globals.db_user}
          db_pass: ${globals.db_pass}
          repl_user: ${globals.repl_user}
          repl_pass: ${globals.repl_pass}
          monitor_user: ${globals.monitor_user}
          monitor_pass: ${globals.monitor_pass}        
          cluster_name: ${settings.envName}
        
      - jps: ${baseUrl}/scripts/geoGlusterManager.jps
        envName: ${globals.master_env_name}
        settings:
          install_gluster_cluster: true

      - jps: ${baseUrl}/scripts/cacheManager.jps
        envName: ${globals.master_env_name}
        settings:
          setup_cache_replication: true

  - else:
    - install:
      - jps: ${baseUrl}/scripts/geoGaleraManager.jps
        envName: ${globals.master_env_name}
        settings:
          install_galera_cluster: true
        
      - jps: ${baseUrl}/scripts/geoGlusterManager.jps
        envName: ${globals.master_env_name}
        settings:
          install_gluster_cluster: true

      - jps: ${baseUrl}/scripts/cacheManager.jps
        envName: ${globals.master_env_name}
        settings:
          setup_cache_replication: true

  - install-wordpress
  - if (${settings.mu_addon:false}):
    - install: ${globals.wp_cluster_url}/addon/WPMU.jps
      envName: ${globals.master_env_name}
      settings:
        mode: subdir

  - if ('${settings.le_addon:false}' == 'true'):
    - setGlobals:
        protocol: https      
    - install:
        jps: https://github.com/jelastic-jps/lets-encrypt/blob/stage/manifest.jps?_r=${fn.random}
        envName: ${settings.envName}-1
        nodeGroup: bl
        skipEmail: true
        settings:
          customDomains: ${globals.domain}
          fallbackToX1: true
          webroot: true
          webrootPath: /var/www/webroot/ROOT
  - if ('${settings.cdn_addon:false}' == 'true'):
    - install:
        jps: https://raw.githubusercontent.com/jelastic-jps/cdn/master/manifest.yml?_r=${fn.random}
        envName: ${settings.envName}-1
        nodeGroup: bl
        skipEmail: true
        settings:
          note: ${settings.noteCDN:}
  - install:
      jps: ${baseUrl}/scripts/cacheManager.jps
      envName: ${globals.master_env_name}
      settings:
        clean_cache: true

actions:

  initGlobals:
    - script: |
        var regions = '${settings.regions}'.split(','),
            envGroups = eval('(' + MANIFEST + ')').envGroups, index,
            onAfterReturn = { setGlobals: {} }, 
            glbs = onAfterReturn.setGlobals,
            resp, domain;
        resp = jelastic.env.control.GetRegions(appid, session);
        if (resp.result != 0) return resp;
        resp = resp.array;
        for (var i = 0, n = regions.length; i < n; i ++) {
          index = i + 1;
          glbs["region-" + index] = regions[i];
          for (var a = 0, b = resp.length; a < b; a++) {
            for (var k = 0, l = resp[a].hardNodeGroups.length; k < l; k++) {
              if (resp[a].hardNodeGroups[k].uniqueName == regions[i])
                glbs["domain-" + index] = resp[a].domain;
            }
          }
          if (index == 1) {
            glbs["displayName-" + index] = "WP Cluster Primary";
            glbs["initial_env_master-" + index] = "true";
          } else {
            glbs["displayName-" + index] = "WP Cluster Secondary " + i;
            glbs["initial_env_master-" + index] = "false";
          }
        }
        glbs["envGroups"] = envGroups;
        glbs["regionsCount"] = regions.length;
        glbs["master_env_name"] = "${settings.envName}-1";
        return { result: 0, onAfterReturn: onAfterReturn };

  createEnvs:
    - script: |
        var regions = '${settings.regions}'.split(','), actions = [];
        var envGroups = eval('(' + MANIFEST + ')').envGroups;
        for (var cluster = 1, n = regions.length + 1; cluster < n; cluster ++) {
          actions.push({
            jps: "${globals.wp_cluster_url}/manifest.yml?_r=${fn.random}",
            envName: "${settings.envName}-" + cluster,
            loggerName: "${settings.envName}-" + cluster,
            envGroups: envGroups,
            displayName: "${globals.displayName-" + cluster + "}",
            region: "${globals.region-" + cluster + "}",
            settings: {
              waf: "${settings.waf}",
              wp_protect: "${settings.wp_protect}",
              le_addon: "false",
              cdn_addon: "false",
              is_install_wp: "false",
              is_db_cluster: "false",
              bl_count: "1",
              ls_admin_pass: "${globals.ls_admin_pass}",
              glusterfs: "true",
              galera: "${globals.initial_env_master-" + cluster + "}",
              db_user: "${globals.db_user}",
              db_pass: "${globals.db_pass}",
              loadGrowth: "${settings.loadGrowth}",
              db_async_topology: "${globals.db_async_topology}"
            }
          });
        }
        return { result: 0, onAfterReturn: { 'marketplace.jps.install': actions } };
  
  getEnvNodes:
    - script: |
        var regions = '${settings.regions}'.split(','),
            onAfterReturn = { setGlobals: {} },
            glbs = onAfterReturn.setGlobals;
        for (var cluster = 1, n = regions.length + 1; cluster < n; cluster ++) {
          var resp = jelastic.env.control.GetEnvInfo('${settings.envName}-' + cluster, session);
          if (resp.result != 0) return resp;
          for (var i = 0, k = resp.nodes; i < k.length; i++) {
            if (k[i].nodeGroup == 'sqldb')
              k[i].ismaster ? glbs["master_id_galera-" + cluster] = k[i].id : 0;
            if (k[i].nodeGroup == 'bl') 
              k[i].ismaster ? glbs["master_id_bl-" + cluster] = k[i].id : 0;
            if (k[i].nodeGroup == 'cp') 
              k[i].ismaster ? glbs["master_id_cp-" + cluster] = k[i].id : 0;
          }
        }
        return { result: 0, onAfterReturn: onAfterReturn };

  installEnvsManager:
    script: |
      var regions = '${settings.regions}'.split(','),
          actions = [];
      for (var cluster = 1, n = regions.length + 1; cluster < n; cluster ++) {
        actions.push({
          jps: "${baseUrl}/scripts/envManager.jps?_r=${fn.random}",
          envName: "${settings.envName}-" + cluster,
          settings: {
              wp_cluster_url: "${globals.wp_cluster_url}",
              db_cluster_url: "${globals.db_cluster_url}",
              master_env_name: "${settings.envName}-1",
              cluster_name: "${settings.envName}",
              galera_server_id: "${globals.galera_server_id}",
              galera_segment_id: cluster,
              db_user: "${globals.db_user}",
              db_pass: "${globals.db_pass}",
              ls_admin_pass: "${globals.ls_admin_pass}",
              envName: "${settings.envName}"
          }
        });
      }
      return { result: 0, onAfterReturn: { install: actions } };

  install-wordpress:
    - install: ${globals.wp_cluster_url}/scripts/installWP.jps
      envName: ${globals.master_env_name}
      settings:
        db_host: ${globals.db_host}
        db_user: ${globals.db_user}
        db_pass: ${globals.db_pass}
        wp_admin_pass: ${globals.wp_admin_pass}
        wp_title: "Hello World"
        wp_url: ${globals.protocol}://${globals.domain}
        version_wordpress: ${globals.version_wordpress}
  
success:
  email: success/email/${globals.regionsCount}-region-${globals.email}.md?_r=${fn.random}
  text: success/text/${globals.regionsCount}-region-${globals.success}.md?_r=${fn.random}

startPage: ${globals.protocol}://${globals.domain}/