User:DiFronzo/Deno

From Wikitech

{{Toolforge nav}}

Overview

Deno JS webservices are supported on Toolforge. Deno JS is an open-source, cross-platform JavaScript/Typescript run-time environment that executes JavaScript code server-side. This tutorial is designed to get a sample Deno JS application installed onto Toolforge as quickly as possible. The application is written using... The guide will teach you how to:

  • Create a new tool
  • Run a Deno JS webservice on Kubernetes

Getting started

Prerequisites

Skills

Accounts

Steps to completion

  • Create a new tool account
  • Create a basic Deno JS webservice
  • Add a configuration file

Step-by-step guide

Step 1: Create a new tool account

  1. Create a new tool account.
    • For the examples in this tutorial, <TOOL NAME> is used to indicate places where your unique tool name is used in another command.
  2. SSH to login.toolforge.org.
    • If your local username is different from your shell account name on Toolforge, you will need to include your Toolforge shell account name in your ssh command (i.e. <shell_username>@login.toolforge.org). See Help:Access for more information on using SSH.
  3. Run become <TOOL NAME> to change to the tool user.

Step 2: Create a basic Deno JS webservice

What is Deno JS?

Deno JS is an open-source, cross-platform JavaScript/Typescript run-time environment that executes JavaScript code server-side.

Create a "My first deno tool!" application

The webservice will run on Kubernetes. Using --backend kubernetes is recommended.

$ mkdir -p $HOME/www/js
Create the $HOME/www/js directory for your application
$ curl -fsSL https://deno.land/install.sh | sh
Install newst version of Deno (this is a local installation and you need to update Deno when needed)
$ cd $HOME/www/js
Change to the new directory.

Place the following code in $HOME/www/js/deno_k8s.sh:

#!/bin/bash

set -e

export DENO_INSTALL="$HOME/.deno"
export PATH="$DENO_INSTALL/bin:$PATH"
exec deno run --allow-net test.ts 8000
$ chmod +x deno_k8s.sh
Make it executable

Place the following code in $HOME/www/js/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: TOOL NAME
  namespace: tool-TOOL NAME
  labels:
    name: TOOL NAME
    toolforge: tool
spec:
  replicas: 1
  selector:
    matchLabels:
      name: TOOL NAME
      toolforge: tool
  template:
    metadata:
      labels:
        name: TOOL NAME
        toolforge: tool
    spec:
      containers:
        - name: webservice
          image: docker-registry.tools.wmflabs.org/wikimedia-buster:latest
          command: [ "/data/project/TOOL NAME/js/deno_k8s.sh", "run" ]
          workingDir: /data/project/TOOL NAME/js/
          env:
            - name: HOME
              value: /data/project/TOOL NAME
          imagePullPolicy: Always
          ports:
            - name: http
              protocol: TCP
              containerPort: 8000
          tty: false
          stdin: false

Note: Change all instances of TOOL NAME with the name of the tool. Place the following code in $HOME/www/js/deno.ts:

import { Application, Router } from 'https://deno.toolforge.org/github/oakserver/oak/main/mod.ts';

const { args } = Deno;
const port = Number(Deno.args[0]);
const app = new Application();

const router = new Router();

router.get('/', (ctx) => {
  ctx.response.body = 'Hello World!';
});

app.use(router.allowedMethods());
app.use(router.routes());

await app.listen({ port });

Note: This is just example application for Deno.

Code on Toolforge must be licensed under an Open Source Initiative (OSI) approved license. See the Right to fork policy for more information on this Toolforge policy.

Start the webservice

$ kubectl apply -f deployment.yaml
Starting webservice. Using the Deployment k8s.

Once the webservice is started, navigate to https://$TOOLNAME.toolforge.org/ in your web browser, and see a 'Hello World!' message. Expected file layout

β”œβ”€β”€ .Deno
    └── *
β”œβ”€β”€ logs
β”œβ”€β”€ replica.my.cnf
β”œβ”€β”€ service.manifest
└── www
    └── js
        β”œβ”€β”€ deno.ts
        β”œβ”€β”€ deployment.yaml
        └── deno_k8s.sh

Troubleshooting

  • Find you container's name by running kubectl get pods and use that name to check your container's logs: kubectl logs -f $MY_CONTAINER_NAME

Update the application code

Make your changes and apply the deployment.yaml again.

$ kubectl apply -f deployment.yaml
Starting webservice. Using the Deployment k8s.

Restart the webservice

 $ kubectl rollout restart deployment <deployment_name>
Restarting webservice... to find deployment_name run $ kubectl get deployment

Once the webservice has restarted, navigate to https://$TOOLNAME.toolforge.org/ in your web browser to see the new landing page.

OAuth 2.0 with PKCE

OAuth is a safe mechanism for authenticating a Wikimedia user in the application. For the basics, read more about OAuth on mediawiki.org.

  1. Register a new OAuth consumer.
  2. As callback URL, use: https://<TOOL NAME>.toolforge.org/auth/mediawiki/callback As contact e-mail address, use the e-mail address linked to your Wikimedia unified account.
  3. Keep the default grant settings ('Request authorization for specific permissions.' with just 'Basic rights' selected)
    • You will be able to use your own account before the consumer has been approved.
  4. Copy the consumer token and secret token values that are generated. These are needed for the config.js file.[note 1]
$ touch $HOME/www/js/.env
$ chmod 600 $HOME/www/js/.env
$ vim $HOME/www/js/.env
consumer_key = "the consumer token value from your OAuth consumer registration";
consumer_secret = "the secret token value from your OAuth consumer registration";
import { Router } from 'https://deno.toolforge.org/github/oakserver/oak/main/mod.ts';
import { AuthorizationCodeGrant } from 'https://deno.toolforge.org/github/DiFronzo/oauth2-deno/main/mod.ts';
import { create } from "https://deno.toolforge.org/github/DiFronzo/pkce-deno/master/mod.ts";

const router = new Router();   

const client = new AuthorizationCodeGrant({
  authorizationEndpointURI: "http://<wiki>/wiki/index.php/Special:OAuth/authorize", // example
  tokenEndpointURI: "http://<wiki>/wiki/index.php/Special:OAuth/token", // example
  clientId: 'MY-CLIENT-ID',
  clientSecret: 'MY-CLIENT-SECRET',
  redirectURI: 'MY-CALLBACK-URL',
  scope: 'MY-SCOPE'
});

const codePair = create();

router.get("/auth/wiki", (context) => {
  context.response.redirect(client.constructAuthorizationRequestURI({
    parameters: {
      state: "123", // Change this https://auth0.com/docs/secure/attack-protection/state-parameters
      code_challenge_method: 'S256',
      code_challenge: codePair.codeChallenge,
    }
  }));
});

router.get("/callback", async (context) => {
    const query = new URLSearchParams(context.request.url.search);
    const code = query.get("code");
    const state = query.get("state"); //You should check state
    let responseApi: any
    if (code) {
        responseApi = await client.requestToken({
            code: code,
            parameters: {
                code_verifier: codePair2.codeVerifier,
                refresh_token: ''
            }
        })
    }
    context.response.redirect("/");
});

See also

References

Notes

  1. ↑ While in testing, and using a basic permission consumer, it is harmless to leave the permissions for the config file as the default. However, best practices is to restrict reading to the required users, hence the chmod 600.

Communication and support

Support and administration of the WMCS resources is provided by the Wikimedia Foundation Cloud Services team and Wikimedia movement volunteers. Please reach out with questions and join the conversation:

Discuss and receive general support
Stay aware of critical changes and plans
Track work tasks and report bugs

Use a subproject of the #Cloud-Services Phabricator project to track confirmed bug reports and feature requests about the Cloud Services infrastructure itself

Read stories and WMCS blog posts

Read the Cloud Services Blog (for the broader Wikimedia movement, see the Wikimedia Technical Blog)

[[Category:Toolforge|Deno]]