Genvid Project

The Genvid Project is a minimalist setup that allows you maximum of flexibility for how to test and deploy your project with Genvid. We will study the HCL format of the file, which we find easier to maintain, but the JSON format is a straight translation of the HCL format [1].

Let’s use the Tutorial project as an example. Here the whole file, but we will explore it section by section [2]:

// *** Preamble *** //

// Version number.  Don't change it.
version = "1.3.0"

// The name of the game. Mostly used as a reference.
name    = "tutorial"

// *** End of Preamble *** //


// *** Game Configuration *** //

// The game job.  The name of the job should match the template name.
job "tutorial" {
  // Services that the game depends before starting
  depends_on = [
    "nats",
    "compose",
  ]
  // Add the job to the default start jobs.
  // It is true by default.
  // autostart = true
}

// The game events map-reduce definition.
event "game" {
  path = "{{env `PROJECTDIR` | js}}\\app\\mr.json"
}

// Log for the game
log "game" {
  task   = "tutorial"
  stdout = false
}

// *** End of Game Configuration *** //


// *** Web Server Configuration *** //

// The web server job
job "web" {
}

// Standard output of the web server
log "web" {
  stdout = true
  task   = "web"
}

// Standard error output of the web server.
log "weberr" {
  stdout = false
  task   = "web"
}

// *** End of WebServer Configuration *** //


// *** Key-Value Store Configuration *** //

config {
  local {
    // Some website configuration, used by the local web job template
    website {
      root   = "{{env `PROJECTDIR` | js}}\\web"
      script = "{{env `PROJECTDIR` | js}}\\web\\bin\\www"
    }

    // The game binary, used by the local game job template
    binary {
      tutorial {
        path = "{{env `PROJECTDIR` | js}}\\app\\x64\\Release\\Tutorial.exe"
      }
      node {
        // Here, we used the C:\Windows\System32\where.exe command here
        // to find the path to node.
        path = "{{plugin `where.exe` `node` | js}}"
      }
    }

    appdir = "{{env `PROJECTDIR` | js}}\\app"
  }

  // The web server image, used by the cloud web job.
  cloud {
    image {
      web {
        tag = "sampleweb:dev"
      }
    }
  }
}

// *** End of Key-Value Store Configuration *** //


// *** Build Scripts Definitions *** //

// The local build script.
script "build" {
  commands = [
    // Build the game
    "\"{{env `PYTHON_EXECUTABLE` | js}}\" \"{{env `PROJECTDIR` | js}}\\run.py\" build",
    // Build the web site
    "\"{{env `PYTHON_EXECUTABLE` | js}}\" \"{{env `PROJECTDIR` | js}}\\web\\build.py\" build",
  ]
}

// The cloud build script
script "build_cloud" {
  commands = [
    // Build the game and archive it for the cloud
    "\"{{env `PYTHON_EXECUTABLE` | js}}\" \"{{env `PROJECTDIR` | js}}\\run.py\" all",
    // Build a docker image of the website
    "\"{{env `PYTHON_EXECUTABLE` | js}}\" \"{{env `PROJECTDIR` | js}}\\web\\build-docker.py\" all",
  ]
}

// *** End of Build Scripts Definitions *** //

// *** Links Configuration *** //

// Links to the web sites
link {
  web {
    name     = "Tutorial Demo"
    template = "http://${service `web`}/"
  }
  admin {
    name     = "Tutorial Admin"
    template = "http://${serviceEx `web` `` true}/admin"
  }
}

// ***End of Links Configuration *** //

Preamble

The first thing at the top is the preamble:

// Version number.  Don't change it.
version = "1.3.0"

// The name of the game. Mostly used as a reference.
name    = "tutorial"

We only have two things here: the version of the configuration (currently 1.3.0), and the name of the project. You must not change the version, which will be used to help future upgrades. The name is free form, but you should try to make it match the name of your main job. The version number represents the first version of the SDK supporting this format.

Game Configuration

The second section contains the configuration for the game job:

// The game job.  The name of the job should match the template name.
job "tutorial" {
  // Services that the game depends before starting
  depends_on = [
    "nats",
    "compose",
  ]
  // Add the job to the default start jobs.
  // It is true by default.
  // autostart = true
}

// The game events map-reduce definition.
event "game" {
  path = "{{env `PROJECTDIR` | js}}\\app\\mr.json"
}

// Log for the game
log "game" {
  task   = "tutorial"
  stdout = false
}

We have three elements in this section: a job, an event, and a log. Let’s check them one by one.

The first element is a new job. The name of the job should match the name of the template file as well as the name of the job in the template file. Job configurations have two attributes:

  • depends_on: a list of service to wait on before starting the job. The default is none.
  • autostart: if the job must be automatically started on a start command without argument.

The next element in the section is the event. Right now, the event can only point to a file using the path attribute, containing a Schema for the Events Channel.

Finally, the log give the default options to pass to the show_alloc_log() command when you run local.py log game.

Web Server Configuration

The third section is very similar to the previous one, but it is for the web server. It contains a single job, with only the default attribute, and two log elements: one for the standard output, and the other for the standard error.

// The web server job
job "web" {
}

// Standard output of the web server
log "web" {
  stdout = true
  task   = "web"
}

// Standard error output of the web server.
log "weberr" {
  stdout = false
  task   = "web"
}

Key-Value Store Configuration

The fourth section contains a single element: config:

config {
  local {
    // Some website configuration, used by the local web job template
    website {
      root   = "{{env `PROJECTDIR` | js}}\\web"
      script = "{{env `PROJECTDIR` | js}}\\web\\bin\\www"
    }

    // The game binary, used by the local game job template
    binary {
      tutorial {
        path = "{{env `PROJECTDIR` | js}}\\app\\x64\\Release\\Tutorial.exe"
      }
      node {
        // Here, we used the C:\Windows\System32\where.exe command here
        // to find the path to node.
        path = "{{plugin `where.exe` `node` | js}}"
      }
    }

    appdir = "{{env `PROJECTDIR` | js}}\\app"
  }

  // The web server image, used by the cloud web job.
  cloud {
    image {
      web {
        tag = "sampleweb:dev"
      }
    }
  }
}

The content of this element is added to the key-value store, each object being considered as a folder, and each value converted to string and inserted as a key.

An interesting element in this section is the usage of a consul-template’s plugin for determining the location of the node.exe binary. The plugin simply calls the executable where (a DOS command) with the argument node, and returns the output of the command. You can use your own executables, as long as they are in your path when the project is loaded. The js method is also quite useful here to ensure elements like Windows path separators are correctly quoted in the file.

We also use the consul-template’s env method, for accessing environment variables. In this case, it uses the PROJECT_DIR variable which always points to the folder of the project file. The template system has access to all environment variables of the scripts, plus some set by the script itself. You can list these variables by running:

py local.py env

The key-value store is always initialized first with this section, then with the key-value found under the <project_dir>\config, and finally with the ones found under the <rootdir>\config, the latest ones overwriting the previous ones.

Build Scripts Definitions

This section contains two script elements. They are used when you run the local.py run-script <name> command. Right now, the two scripts present here are only used for building the binaries, one for the local deployment (build), and the other for the cloud (build_cloud):

// The local build script.
script "build" {
  commands = [
    // Build the game
    "\"{{env `PYTHON_EXECUTABLE` | js}}\" \"{{env `PROJECTDIR` | js}}\\run.py\" build",
    // Build the web site
    "\"{{env `PYTHON_EXECUTABLE` | js}}\" \"{{env `PROJECTDIR` | js}}\\web\\build.py\" build",
  ]
}

// The cloud build script
script "build_cloud" {
  commands = [
    // Build the game and archive it for the cloud
    "\"{{env `PYTHON_EXECUTABLE` | js}}\" \"{{env `PROJECTDIR` | js}}\\run.py\" all",
    // Build a docker image of the website
    "\"{{env `PYTHON_EXECUTABLE` | js}}\" \"{{env `PROJECTDIR` | js}}\\web\\build-docker.py\" all",
  ]
}

Right now, script elements have a single attribute, commands, which contains a list of commands to be run. The command are interpreted directly with the local shell, so you could put environment variables in it directly, or even redirection. In our case, we opted to let consul-template do the substitution and used PYTHON_EXECUTABLE to ensure the Python interpreter is the same one used for running the local.py script. We could have simply used the py launcher instead.

[1]JSON is also the format we used when we saved it under config/_local.json, so don’t hesitate to take a look there if you want to see the result of a transformation.
[2]This division by section is purely to help the presentation. You don’t have to respect this order.