GithubHelp home page GithubHelp logo

automattic / dserve Goto Github PK

View Code? Open in Web Editor NEW
23.0 8.0 5.0 769 KB

a docker development server for painless testing

TypeScript 98.64% Makefile 0.16% JavaScript 0.27% Dockerfile 0.93%
docker containers

dserve's Introduction

dserve

CircleCI

A development server for serving branches of your docker-based web application an on-demand basis.

It can build images for any hash, run containers, stop containers that haven't been accessed in a while, proxy requests to the right container based on query params, etc.

Install

git clone [email protected]:Automattic/dserve.git
cd dserve
nvm use
yarn
yarn start

Use

You will need to modify your hosts file to include the following line:

127.0.0.1 calypso.localhost

Then you may either specify a branch or a hash to load.

  1. branch: calypso.localhost:3000?branch={branchName}
  2. hash: calypso.localhost:3000?hash={commitHash}

Source Code Overview

At the end of the day, dserve is node express server written in typescript. It can trigger docker image builds and deletions, start and stop containers, and a few other tricks.

Here is an example flow of what happens when requesting a never-requested-before commit sha:

  1. User tries to access https://calypso.live?hash=hash.
  2. dserve will query the local fs and docker daemon to determine the status of the corresponding image. It will discover that hash has never been requested before and needs to build an image for it. Therefore it will add the hash to the build queue and send the user a screen saying "starting a build for requested hash".
  3. If the branch or hash exist, dserve will redirect the user to https://hash-$hash.calypso.live, isolating the build to a subdomain
  4. Internally dserve checks the build queue very frequently and will initate a build within seconds. The build takes places within its own temporary directory in a place like: /tmp/dserve-calyspo-hash/repo and logs will be stored in /tmp/dserve-calypso-hash/dserve-build-log.txt.
  5. When a user requests the branch while the build is happening, dserve will recognize that the build is in progress and show the user the build's status.
  6. Finally when the build completes, the next time a user requests the branch they will see: "starting container, this page will refresh in a couple of seconds".

index.ts: this acts as the entry point for dserve. it sets up the server, initializes the routes, and contains the request handling for each incoming route.

middlewares.ts this file contains all of the middlewares that dserve uses.

  1. redirectHashFromQueryStringToSubdomain: This middleware will look for a branch or hash in the query string and redirect to a corresponding subdomain matching the commit hash.
  2. determineCommitHash: Every request to dserve needs to be associated with a commit hash or else it cannot be fulfilled. this middleware will attach a commitHash to the express request based on the subdomain.
  3. session: Standard session middleware so that each request doesn't need to specify a hash with a query param.

api.ts: Contains all of the code that interfaces with external things like the fs, docker, or github. there are two kinds of entities that exist in this file, those that periodically update data and the other is helper functions for things that need to be done on-demand.

periodically repeating: updating the git branches to commit hash mapping, updating which docker images are available from the local docker server, stopping unused containers.

on-demand helper functions: this includes functionality for recording how recently a commitHash was accessed, a helper for proxying a request to the right container, helpers for checking the progress/state of a a commit, etc.

builder.ts: Contains all of the code for building the docker images for a specific commit hash. This includes making build queue and rate limiting dserve to N builds at a time.

logger.ts: Exports a couple key items around loggers including the application logger and a getter for configuring a specific logger per-docker build of commits.

Operations

Are you in a situation where you are suddently tasked with maintaining dserve even though you didn't write it? Once the flood of mixed feelings towards the original authors settles, it'd be a good idea to read this section. You probably want to know how to do things like, deploy new code, debug issues, and e2e test dserve locally. Here goes nothing:

deploying This GitHub repo is polled every 15 minutes. If there have been updates to the repo, then the latest sha is deployed. Thats it. Merge, and it'll be deployed. In a high severity situation where dserve is broken, you'll want to make sure you time your attempts to fix it before the next 15 minute mark.

debugging dserve has a couple helpful urls for debugging issues for times when you don't have ssh access. Note that any time you see branch=${branchName} you can subsitute hash=${sha}.

e2e test locally

  1. start up dserve with yarn start
  2. try to access a branch that you've never built before by going to localhost:3000?branch=${branchName}. After a successful build you should be proxied to calypso
  3. try to access an already built branch (by looking at the result of docker images you can find repo-tags with the right sha to specify). After a succesfful build you should be proxied to that branch's version of calypso.
  4. you might need access to the private Docker registry: PCYsg-stw-p2

fixing errors

things that have broken in the past

  1. We were running an older version of docker that had buildCache issues. disabling the build cache (as a setting in the buildImage function) until we could upgrade docker versions solved the issue
  2. The Docker Daemon ran into problems: there was one instance where builds seemed to just hang randomly and there was no obvious cause. all builds had failed. Systems restarting the docker daemon solved the issue.
  3. Double slash branches: there is an interesting property of git with respect to how branches get stored on the local filesystem. Each slash in a branchname actually means that it occupies a nested folder. That means if locally you have a branch named thing/thing2 then you cannot pull down a remote branch with the name thing. The reason the remote repo was capable of having branch thing/thing2 is because thing had already been deleted in its repo. The fix here is to always run a git prune when pulling down new branches which automatically deletes the appropriates local branches that no longer exist in the remote repo.

dserve's People

Contributors

alisterscott avatar blowery avatar chaosexanima avatar designsimply avatar dmsnell avatar griffbrad avatar renovate[bot] avatar samouri avatar scinos avatar sirreal avatar yashwin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dserve's Issues

"Could not fetch repo to update branches list" error

This is being logged repeatedly:

{"name":"dserve","hostname":"dserve-304-37.dca.wordpress.com","pid":123456,"level":50,"err":{"message":"authentication required but no callback set","name":"Error","stack":"Error: authentication required but no callback set"},"msg":"Could not fetch repo to update branches list","time":"2020-05-03T14:34:07.108Z","src":{"file":"/home/dserve/src/src/api.ts","line":289},"v":0}

update image tag to include branch

Systems would enjoy having the branch accessible via the image tag when possible.

The new tagnames should be:

  1. if branch + hash available: tagname=sha-branch
  2. if only hash is available: tagname=sha

protocol when loading up a branch:

search for any image tagged with the right sha irrespective of selected branch.

Node 12.x compatibilty

Currently dserve is not compatible with the latest LTS node release. Can we bump the dependencies so it is? I think the major problem, as usual, is nodegit :)

nodegit segfault when cloning repo

The current version of nodegit required by dserve, 0.23.1 segfaults when trying to clone a remote repo. This is reproducible on Debian Stable (10) using this example code from nodegit


var console = require("console");
console.log( "Here, before clone");
// Clone a given repository into the `./tmp` folder.
Git.Clone("https://github.com/nodegit/nodegit", "./tmp")
  // Look up this known commit.
  .then(function(repo) {
    // Use a known commit sha from this repository.
	console.log( "Here, in commit lookup");
    return repo.getCommit("59b20b8d5c6ff8d09518454d4dd8b7b30f095ab5");
  })
  // Look up a specific file within that commit.
  .then(function(commit) {
	console.log( "Here, in clone");
    return commit.getEntry("README.md");
  })
  // Get the blob contents from the file.
  .then(function(entry) {
    // Patch the blob to contain a reference to the entry.
    return entry.getBlob().then(function(blob) {
      blob.entry = entry;
      return blob;
    });
  })
  // Display information about the blob.
  .then(function(blob) {
    // Show the path, sha, and filesize in bytes.
    console.log(blob.entry.path() + blob.entry.sha() + blob.rawsize() + "b");

    // Show a spacer.
    console.log(Array(72).join("=") + "\n\n");

    // Show the entire file.
    console.log(String(blob));
  })
  .catch(function(err) { console.log(err); });

Because of this segfault, bootstrapping dserve is impossible (it will never be able to close the wp-calypso repo. It looks like the latest version of nodegit fixes the segfault, but there are other errors when trying to use it - it seems like there might be some API changes.

dserve-304-37 yarn[41746]: {"name":"dserve","hostname":"dserve-304-37.dca.wordpress.com","pid":41779,"level":50,"err":{"message":"Callback is required and must be a Function.","name":"Error","stack":"Error: Callback is required and must be a Function.\n    at /home/dserve/src/node_modules/nodegit-promise/lib/node-extensions.js:24:20\n    at tryCallTwo (/home/dserve/src/node_modules/nodegit-promise/lib/core.js:45:5)\n    at doResolve (/home/dserve/src/node_modules/nodegit-promise/lib/core.js:173:13)\n    at new Promise (/home/dserve/src/node_modules/nodegit-promise/lib/core.js:65:3)\n    at Repository.getReferences (/home/dserve/src/node_modules/nodegit-promise/lib/node-extensions.js:19:12)\n    at /home/dserve/src/build/api.js:221:52\n    at Generator.next (<anonymous>)\n    at fulfilled (/home/dserve/src/build/api.js:5:58)\n    at process._tickCallback (internal/process/next_tick.js:68:7)"},"repository":"Automattic/wp-calypso","msg":"Error creating branchName --> commitSha map","time":"2020-05-02T22:34:13.388Z","src":{"file":"/home/dserve/src/src/api.ts","line":326},"v":0}

Updating nodegit is probably required for #156 so trying to fix this segfault probably isn't worth it, but wanted to document it here for completeness.

Builds with a pinned Docker label fail mysteriously

When trying to build the original code in Automattic/wp-calypso#25321, dserve would bomb with the following log:

Time=2018-06-06T20:08:01.651Z | ---------------- DOCKER START ----------------
Time=2018-06-06T20:08:07.378Z | Step 1/16 : FROM node:8.11.2@sha256:321655aeb195b7946e9a29d28453388751389e8ced66b4dea772ae76a6985309
Time=2018-06-06T20:08:07.902Z | { status: 'Pulling from library/node', id: '8.11.2' }
Time=2018-06-06T20:08:08.182Z | Encountered error when building image

That PR was trying to pin the docker images to make builds more stable. Currently we're using labels from docker, which can change as the underlying OS takes patches.

Renovate (the new dependency updating tool) takes care of creating new PRs as the docker image associated with the label changes.

It's not critical that this get fixed (we decided to stick with plain labels, they've worked just fine so far), but it might be good to understand what went wrong.

Multiple simultaneous requests result in HTTP 304

Certain test suites in the Playwright E2E project intermittently encounter HTTP 304 status when first attempting to load a calypso URL.

The failure is presumably occurring here when the <baseCalypsoURL>/log-in endpoint is being hit by multiple containers at the same time. Retrying (automatically via magellan) usually results in the next iteration being able to access the endpoint as normal.

Slack thread: p1624428683011700-slack-C01BVMH5C20

Branch redirection drops query args

We currently use bots to provide the live links like this:

https://calypso.live/?branch=update/tiled-gallery-margins

It's often handy to provide links directly to a set up for testing, which may include path and additional query arguments. When the branch query is rewritten to the hash url, other query args are dropped. For example:

https://calypso.live/block-editor/post?branch=add/tiled-grid-package&flags=jetpack/blocks/beta

Winds up at (notice no flags arg):

https://hash-46d13ec811227b549777d82deb8a02736299c4ac.calypso.live/block-editor/post

Maintain unrecognized query args when redirecting.

Open source this project?

Do we have any secrets or private config options?

If not, we should make this repo public โ€” would be fun to share it, talk about it at user groups, etc.

Overzealous logging

While on the surface it looks to be throttled to once per minute, in practice we've sometimes seen the message There are images waiting to be built that are stuck because of too many concurrent builds tens of times per second.

One container per sha

as of now, dserve creates a new container every time a request comes in for a branch that doesn't have a running container. We end up creates multiple containers for the same image. On top of that, if for whatever reason the container fails to start properly, we will keep looping and create a ton of containers for that image.

Possible solutions:

  1. always delete a container when we are done with it (that means after not being used for awhile or if it failed to start to begin with).
  2. reuse existing stopped containers instead of starting new ones.

Add FailedBuild status for faster error detection

Currently the canary tests that run against dserve don't have any way to detect when an image fails to build, so they need to wait for the full 10-minute timeout to report failure back to the PR. It would be helpful to have the /status endpoint register that there was a failure, and even provide the error message (i.e Encountered error when building image vs Encountered error while git checking out, tarballing, or handing to docker) for display on the CircleCI output

respect dockerignore

As @sirreal discovered, .dockerignore files are currently ignored by the tar stream process in the builder. We should respect .dockerignore files when building the build context

Dependency Dashboard

This issue provides visibility into Renovate updates and their statuses. Learn more

Rate Limited

These updates are currently rate limited. Click on a checkbox below to force their creation now.

  • Update Node.js to v16 (node, @types/node)
  • Update Node.js to v17
  • Update dependency @types/react to v18
  • Update dependency @types/react-dom to v18
  • Update dependency get-port to v6
  • Update dependency hot-shots to v9
  • Update dependency strip-ansi to v7

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.


  • Check this box to trigger a request for Renovate to run again on this repository

gutenberg support

should be pretty trivial to get gutenberg running on dserve.
just need to make a Dockerfile for https://github.com/WordPress/gutenberg, and add some configuration options.

Open question:
wheres the most graceful place to put configs? To start it could all be env variables or local json configs.
in some distant future we could expect a .dserverc in build dirs

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.