Skip to content

Instantly share code, notes, and snippets.

@djmitche
Last active June 22, 2018 03:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save djmitche/010023b33f05bd57752397a21e0763e7 to your computer and use it in GitHub Desktop.
Save djmitche/010023b33f05bd57752397a21e0763e7 to your computer and use it in GitHub Desktop.
upgrading services to use tc-lib-* 10.x

Upgrading

For the most part, the error messages will tell you what's wrong. The changes that affect production are fairly straightforward. The changes that affect testing are a little more situationally specific, since each service's tests are uniquely unique (until RFC0110 anyway).

Upgrades

Upgrade all taskcluster-lib-*, pulse-publisher, and taskcluster-client to the latest, which should be 10.0.0 or higher. Upgrade azure-entities, if used, to 4.0.0 or higher; similarly azure-blob-storage at 3.0.0 or higher.

config.yml:

Configure taskcluster.rootUrl to !env TASKCLUSTER_ROOT_URL in the defaults in config.yml. Do not configure a special rootUrl for testing. Set TASKCLUSTER_ROOT_URL to https://taskcluster.net in Heroku.

Remove baseUrl, authBaseUrl, and publicUrl everywhere.

Configure Azure like this (updating any env vars in Heroku as necessary). Don't forget to make parallel changes in the test section!

defaults:
  azure:
    accountId:                    !env AZURE_ACCOUNT
    cryptoKey:                    !env AZURE_CRYPTO_KEY
    signingKey:                   !env AZURE_SIGNING_KEY

note it's now accountId instead of accountName or account.

Add TASKCLUSTER_ROOT_URL=https://taskcluster.net to the secrets used to test the service, alongside the clientId and accessToken.

If you are using pulse, note that pulse now requires four parameters:

    pulse:
        username: !env PULSE_USERNAME
        password: !env PULSE_PASSWORD
        hostname: !env PULSE_HOSTNAME
        vhost: !env PULSE_VHOST

(and you will need to add PULSE_HOSTNAME=pulse.mozilla.org and PULSE_VHOST=/ to Heroku configs)

api.js or v1.js

See taskcluster-secrets for an example.

The taskcluster-lib-api library now provides an API builder, which main.js turns into an API. So change API to APIBuilder and api to builder. Ensure you are providing a serviceName (that is, without taskcluster-) and version (v1) to the APIBuilder constructor. Remove schemaPrefix. The name is no longer allowed as of bug 1463207

Replace all input and output references with the actual filename, e.g., create-frob.yml instead of create-frob.json or anything else. Do not include v1/ -- that will be added automatically.

exchanges.js

If your app sends Pulse messages, you'll need to change this file as well. Remove all schemaPrefix, referencePrefix, and exchangePrefix. Add serviceName, projectName, and version to the new Exchanges({..}) options. Change all schema: to indicate a bare filename, ending in .yml. Again, do not include v1, as that will be added automatically.

schemas/

Move all schema files from schemas/ into schemas/v1/. Except don't move constants.yml - that must remain in the schemas directory.

Any schemas with a $ref should use a relative URI. The reference is always to another schema document in the same directory (we prohibit refs between services), so just use something like {$ref: "foo-definition.json#"}.

main.js

See taskcluster-secrets for a worked example of main.js.

Require taskcluster-lib-app as App. Require taskcluster-lib-validate as SchemaSet. Change the require for api.js or v1.js to assign to builder. Add a dependency on taskcluster-lib-azure, if using Azure, and require it:

const {sasCredentials} = require('taskcluster-lib-azure'); 

In the monitor constructor, pass a projectName instead of project - this shoud be the full name of the service (taskcluster-foo). Pass rootUrl: cfg.taskcluster.rootUrl too.

Change the validator component to schemaset and use new SchemaSet. Pass serviceName (the short name). Do not pass a prefix.

  schemaset: {
    requires: ['cfg'],
    setup: ({cfg}) => new SchemaSet({
      serviceName: 'foo',
      publish: cfg.app.publishMetaData,
      aws: cfg.aws,
    }),
  },

You will want to replace validator with schemaset everywhere it appears, including where it is passed to exchanges.setup and builder.build.

To set up an Azure entity, call sasCredentials with the necessary four arguments. Note that the property names have changed (tableName and accountId), and that the table name must be given twice (once for the SAS credentials, once for the table access):

  data.MyEntity.setup({
    tableName: cfg.app.myTableName,
    credentials: sasCredentials({
      accountId: cfg.azure.accountId,
      tableName: cfg.app.myTableName,
      rootUrl: cfg.taskcluster.rootUrl,
      credentials: cfg.taskcluster.credentials,
    }),
  });

To set up the API, modify the api section of the loader to call builder.build, passing a rootUrl and removing the authBaseurl, baseUrl, and referencePrefix options.

The server component should look like this:

  server: {
    requires: ['cfg', 'api', 'docs'],
    setup: ({cfg, api, docs}) => App({
      port: cfg.server.port,
      env: cfg.server.env,
      forceSSL: cfg.server.forceSSL,
      trustProxy: cfg.server.trustProxy,
      apis: [api],
    }),
  },

Specifically, it is passing the api component in the list of apis. It is still necessary to require docs, but it is unused. It is not necessary to call app.use and createServer.

If you have a publisher, remove options exchangePrefix and referencePrefix and add rootUrl. Require the schemaset component and change the validator parameter to await schemaset.validator(cft.taskcluster.rootUrl),.

  publisher: {
    requires: ['cfg', 'monitor', 'schemaset'],
    setup: async ({cfg, monitor, schemaset}) => exchanges.setup({
      rootUrl:            cfg.taskcluster.rootUrl,
      credentials:        cfg.pulse,
      validator:          await schemaset.validator(cfg.taskcluster.rootUrl),
      publish:            cfg.app.publishMetaData,
      aws:                cfg.aws,
      monitor:            monitor.prefix('publisher'),
    }), 
  },  

For the reference component (passed to docs), remove exchangePrefix and add rootUrl.

  reference: {
    requires: ['cfg'],
    setup: ({cfg}) => exchanges.reference({
      rootUrl:          cfg.taskcluster.rootUrl,
      credentials:      cfg.pulse,
    }), 
  },

In the docs component, references will come from the builder module (not a component) and you'll need to pass schemaset rather than validator.schemas:

  docs: {
    requires: ['cfg', 'schemaset'],
    setup: ({cfg, schemaset}) => docs.documenter({
      // ... 
      schemaset,
      references: [
        {name: 'api', reference: builder.reference()},
      ],
    }),
  },

Misc

If the esrvice uses a pulse listener client, e.g., QueueEvents, note that it must now receive rootUrl as a constructor arg: new QueueEvents{{rootUrl: cfg.taskcluster.rootUrl}).

Iterate

If you can typically start this service with something like NODE_ENV=test TASKCLUSTER_ROOT_URL=https://tc.example.com node src/main.js server, do so now and solve the errors that come up. Just about any no-longer-used parameter, or newly-required parameter, has a clear error message if it's wrong. Once that's done, set to work on the tests..

Tests

This is a great time to make the tests more RFC0110 compliant! It's quite likely that's the easiest path.

Note that taskcluster-hooks and taskcluster-secrets are both upgraded to RFC0110 and provide some useful boilerplate. Have a look at the taskcluster-lib-testing docs, too -- stickyLoader and Secrets can be quite useful. Once nice thing to note is the definition and use of helper.withEntity and helper.withServer which can take care of setting up Azure entities and a fake webserver for API testing.

One of the trickiest bits about testing now is that there are potentially two rootUrls at play: a fake webserver is running the API under test at http://localhost:9857, and the fakeauth should be configured to fake auth.authenticateHawk on that rootUrl. But when using "real" Azure tables, you will need to initialize the Azure entity with the rootUrl given in TASKCLUSTER_ROOT_URL, while still running everything else with the localhost rootUrl.

Also of note, App from taskclutser-lib-app now returns an Express Server object (not an app) and does so via a Promise. You may also need to modify any API setup just like in main.js.

If you use the schema utilty from taskcluster-lib-testing, note that it now takes a schemasetOptions argument (pass folder and serviceNam) and requires a full schema id, under the test rootUrl. That will be something like https://tc-tests.localhost/schemas/test/case1.json# -- note carefully the .json# suffix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment