0.4.5 • Published 18 days ago

@backstage/plugin-jenkins-backend v0.4.5

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
18 days ago

Jenkins Plugin (Alpha)

Welcome to the Jenkins backend plugin! Website: https://jenkins.io/

This is the backend half of the 2 Jenkins plugins and is responsible for:

  • finding an appropriate instance of Jenkins for an entity
  • finding the appropriate job(s) on that instance for an entity
  • connecting to Jenkins and gathering data to present to the frontend

Integrating into a backstage instance

This plugin needs to be added to an existing backstage instance.

Typically, this means creating a src/plugins/jenkins.ts file and adding a reference to it to src/index.ts

jenkins.ts

import {
  createRouter,
  DefaultJenkinsInfoProvider,
} from '@backstage/plugin-jenkins-backend';
import { CatalogClient } from '@backstage/catalog-client';
import { Router } from 'express';
import { PluginEnvironment } from '../types';

export default async function createPlugin({
  logger,
  config,
  discovery,
}: PluginEnvironment): Promise<Router> {
  const catalog = new CatalogClient({ discoveryApi: discovery });

  return await createRouter({
    logger,
    jenkinsInfoProvider: DefaultJenkinsInfoProvider.fromConfig({
      config,
      catalog,
    }),
  });
}

This plugin must be provided with a JenkinsInfoProvider, this is a strategy object for finding the Jenkins instance and job for an entity.

There is a standard one provided, but the Integrator is free to build their own.

DefaultJenkinsInfoProvider

Allows configuration of either a single or multiple global Jenkins instances and annotating entities with the job name on that instance (and optionally the name of the instance).

Example - Single global instance

The following will look for jobs for this entity at https://jenkins.example.com/job/teamA/job/artistLookup-build

Config

jenkins:
  baseUrl: https://jenkins.example.com
  username: backstage-bot
  apiKey: 123456789abcdef0123456789abcedf012

Catalog

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: artist-lookup
  annotations:
    'jenkins.io/job-full-name': teamA/artistLookup-build

The old annotation name of jenkins.io/github-folder is equivalent to jenkins.io/job-full-name

Example - Multiple global instances

The following will look for jobs for this entity at https://jenkins-foo.example.com/job/teamA/job/artistLookup-build

Config

jenkins:
  instances:
    - name: default
      baseUrl: https://jenkins.example.com
      username: backstage-bot
      apiKey: 123456789abcdef0123456789abcedf012
    - name: departmentFoo
      baseUrl: https://jenkins-foo.example.com
      username: backstage-bot
      apiKey: 123456789abcdef0123456789abcedf012

Catalog

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: artist-lookup
  annotations:
    'jenkins.io/job-full-name': departmentFoo:teamA/artistLookup-build

If the departmentFoo: part is omitted, the default instance will be assumed.

The following config is an equivalent (but less clear) version of the above:

jenkins:
  baseUrl: https://jenkins.example.com
  username: backstage-bot
  apiKey: 123456789abcdef0123456789abcedf012
  instances:
    - name: departmentFoo
      baseUrl: https://jenkins-foo.example.com
      username: backstage-bot
      apiKey: 123456789abcdef0123456789abcedf012

Custom JenkinsInfoProvider

An example of a bespoke JenkinsInfoProvider which uses an organisation specific annotation to look up the Jenkins info (including jobFullName):

class AcmeJenkinsInfoProvider implements JenkinsInfoProvider {
  constructor(private readonly catalog: CatalogClient) {}

  async getInstance(opt: {
    entityRef: EntityName;
    jobFullName?: string;
  }): Promise<JenkinsInfo> {
    const PAAS_ANNOTATION = 'acme.example.com/paas-project-name';

    // lookup pass-project-name from entity annotation
    const entity = await this.catalog.getEntityByName(opt.entityRef);
    if (!entity) {
      throw new Error(
        `Couldn't find entity with name: ${stringifyEntityRef(opt.entityRef)}`,
      );
    }

    const paasProjectName = entity.metadata.annotations?.[PAAS_ANNOTATION];
    if (!paasProjectName) {
      throw new Error(
        `Couldn't find paas annotation (${PAAS_ANNOTATION}) on entity with name: ${stringifyEntityRef(
          opt.entityRef,
        )}`,
      );
    }

    // lookup department and team for paas project name
    const { team, dept } = this.lookupPaasInfo(paasProjectName);

    const baseUrl = `https://jenkins-${dept}.example.com/`;
    const jobFullName = `${team}/${paasProjectName}`;
    const username = 'backstage-bot';
    const apiKey = this.getJenkinsApiKey(paasProjectName);
    const creds = btoa(`${username}:${apiKey}`);

    return {
      baseUrl,
      headers: {
        Authorization: `Basic ${creds}`,
      },
      jobFullName,
    };
  }

  private lookupPaasInfo(_: string): { team: string; dept: string } {
    // Mock implementation, this would get info from the paas system somehow in reality.
    return {
      team: 'teamA',
      dept: 'DepartmentFoo',
    };
  }

  private getJenkinsApiKey(_: string): string {
    // Mock implementation, this would get info from the paas system somehow in reality.
    return '123456789abcdef0123456789abcedf012';
  }
}

No config would be needed if using this JenkinsInfoProvider

A Catalog entity of the following will look for jobs for this entity at https://jenkins-departmentFoo.example.com/job/teamA/job/artistLookupService

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: artist-lookup
  annotations:
    'acme.example.com/paas-project-name': artistLookupService

Jenkins' terminology notes

The domain model for Jenkins is not particularly clear but for the purposes of this plugin the following model has been assumed:

Jenkins contains a tree of jobs which have children of either; other jobs (making it a folder) or builds (making it a project). Concepts like pipeline and views are meaningless (pipelines are just jobs for our purposes, views are (as the name suggests) just views of subsets of jobs)

A job full name is a slash separated list of the names of the job, and the folders which contain it. For example teamA/artistLookupService/develop, and the same way that a filesystem path has folders and file names.

0.4.5

21 days ago

0.4.4

24 days ago

0.4.4-next.1

1 month ago

0.4.4-next.0

1 month ago

0.4.3

1 month ago

0.4.1

2 months ago

0.4.2

2 months ago

0.4.0

2 months ago

0.4.0-next.2

2 months ago

0.4.0-next.1

2 months ago

0.3.7

2 months ago

0.4.0-next.0

2 months ago

0.3.6

3 months ago

0.3.5

3 months ago

0.3.4

3 months ago

0.3.4-next.3

3 months ago

0.3.4-next.2

3 months ago

0.3.4-next.1

3 months ago

0.3.4-next.0

4 months ago

0.3.3

4 months ago

0.3.3-next.2

4 months ago

0.3.3-next.1

4 months ago

0.3.3-next.0

5 months ago

0.3.2

5 months ago

0.3.2-next.3

5 months ago

0.3.2-next.2

5 months ago

0.3.2-next.1

5 months ago

0.2.5-next.0

9 months ago

0.2.9-next.2

7 months ago

0.2.8-next.1

7 months ago

0.2.8-next.0

8 months ago

0.3.1-next.2

6 months ago

0.3.1-next.0

7 months ago

0.3.1-next.1

6 months ago

0.2.3-next.2

9 months ago

0.2.3-next.1

9 months ago

0.2.3-next.0

10 months ago

0.3.0

7 months ago

0.3.1

6 months ago

0.3.2-next.0

6 months ago

0.2.6-next.1

8 months ago

0.2.6-next.2

8 months ago

0.2.6-next.3

8 months ago

0.2.7

8 months ago

0.2.6

8 months ago

0.2.8

7 months ago

0.2.3

9 months ago

0.2.2

10 months ago

0.2.5

9 months ago

0.2.4

9 months ago

0.2.2-next.0

11 months ago

0.2.1-next.2

11 months ago

0.2.1

11 months ago

0.1.35-next.1

1 year ago

0.2.1-next.1

12 months ago

0.2.1-next.0

12 months ago

0.2.0

12 months ago

0.1.35-next.0

1 year ago

0.1.34-next.3

1 year ago

0.1.34

1 year ago

0.1.34-next.0

1 year ago

0.1.34-next.1

1 year ago

0.1.34-next.2

1 year ago

0.1.33-next.0

1 year ago

0.1.33-next.2

1 year ago

0.1.33-next.1

1 year ago

0.1.32

1 year ago

0.1.33

1 year ago

0.1.30-next.1

1 year ago

0.1.30-next.2

1 year ago

0.1.32-next.0

1 year ago

0.1.32-next.1

1 year ago

0.1.32-next.2

1 year ago

0.1.30

1 year ago

0.1.31

1 year ago

0.1.29-next.0

1 year ago

0.1.29-next.1

1 year ago

0.1.29-next.2

1 year ago

0.1.29-next.3

1 year ago

0.1.28-next.0

2 years ago

0.1.28-next.1

2 years ago

0.1.30-next.0

1 year ago

0.1.27

2 years ago

0.1.28

1 year ago

0.1.29

1 year ago

0.1.27-next.2

2 years ago

0.1.27-next.1

2 years ago

0.1.27-next.0

2 years ago

0.1.26-next.0

2 years ago

0.1.26-next.1

2 years ago

0.1.26-next.2

2 years ago

0.1.26-next.3

2 years ago

0.1.26

2 years ago

0.1.24-next.3

2 years ago

0.1.24-next.1

2 years ago

0.1.24-next.2

2 years ago

0.1.24-next.0

2 years ago

0.1.25-next.1

2 years ago

0.1.25-next.0

2 years ago

0.1.23

2 years ago

0.1.24

2 years ago

0.1.25

2 years ago

0.1.23-next.2

2 years ago

0.1.23-next.1

2 years ago

0.1.23-next.0

2 years ago

0.1.22

2 years ago

0.1.22-next.1

2 years ago

0.1.22-next.0

2 years ago

0.1.18-next.0

2 years ago

0.1.20

2 years ago

0.1.21

2 years ago

0.1.18

2 years ago

0.1.19

2 years ago

0.1.20-next.2

2 years ago

0.1.20-next.1

2 years ago

0.1.20-next.0

2 years ago

0.1.17

2 years ago

0.1.10

2 years ago

0.1.11

2 years ago

0.1.12

2 years ago

0.1.13

2 years ago

0.1.14

2 years ago

0.1.15

2 years ago

0.1.13-next.0

2 years ago

0.1.11-next.0

2 years ago

0.1.12-next.0

2 years ago

0.1.16

2 years ago

0.1.8

2 years ago

0.1.7

2 years ago

0.1.9

2 years ago

0.1.6

3 years ago

0.1.5

3 years ago

0.1.4

3 years ago

0.1.3

3 years ago

0.1.2

3 years ago

0.1.1

3 years ago