Parse as much of the config as possible at start time.

Signed-off-by: Andrew Shirley <andrew.shirley@sainsburys.co.uk>
Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
Andrew Shirley
2021-07-06 18:21:22 +01:00
committed by blam
parent 76fcd5ab40
commit b240ef73e2
5 changed files with 112 additions and 42 deletions
+5 -2
View File
@@ -30,11 +30,14 @@ export default async function createPlugin({
config,
discovery,
}: PluginEnvironment): Promise<Router> {
const catalogClient = new CatalogClient({ discoveryApi: discovery });
const catalog = new CatalogClient({ discoveryApi: discovery });
return await createRouter({
logger,
jenkinsInfoProvider: new DefaultJenkinsInfoProvider(catalogClient, config),
jenkinsInfoProvider: DefaultJenkinsInfoProvider.fromConfig({
config,
catalog,
}),
});
}
```
+5 -1
View File
@@ -15,7 +15,11 @@ export function createRouter(options: RouterOptions): Promise<express.Router>;
// @public
export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider {
constructor(catalog: CatalogClient, config: Config);
// (undocumented)
static fromConfig(options: {
config: Config;
catalog: CatalogClient;
}): DefaultJenkinsInfoProvider;
// (undocumented)
getInstance(opt: {
entityRef: EntityName;
@@ -36,11 +36,14 @@ describe('DefaultJenkinsInfoProvider', () => {
Promise.resolve(entityData as Entity),
);
return new DefaultJenkinsInfoProvider(mockCatalog, config);
return DefaultJenkinsInfoProvider.fromConfig({
config,
catalog: mockCatalog,
});
}
it('Handles entity not found', async () => {
const provider = configureProvider({}, undefined);
const provider = configureProvider({ jenkins: {} }, undefined);
await expect(provider.getInstance({ entityRef })).rejects.toThrowError();
expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef);
@@ -41,7 +41,7 @@ export interface JenkinsInfo {
}
/**
* Use default config and annotations.
* Use default config and annotations, build using fromConfig static function.
*
* This will fallback through various deprecated config and annotation schemes.
*/
@@ -49,11 +49,26 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider {
static readonly OLD_JENKINS_ANNOTATION = 'jenkins.io/github-folder';
static readonly NEW_JENKINS_ANNOTATION = 'jenkins.io/job-slug';
constructor(
private constructor(
private readonly config: {
name: string;
baseUrl: string;
username: string;
apiKey: string;
}[],
private readonly catalog: CatalogClient,
private readonly config: Config,
) {}
static fromConfig(options: {
config: Config;
catalog: CatalogClient;
}): DefaultJenkinsInfoProvider {
return new DefaultJenkinsInfoProvider(
this.loadConfig(options.config),
options.catalog,
);
}
async getInstance(opt: {
entityRef: EntityName;
jobName?: string;
@@ -99,15 +114,13 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider {
this.config,
);
const baseUrl = instanceConfig.getString('baseUrl');
const username = instanceConfig.getString('username');
const apiKey = instanceConfig.getString('apiKey');
const creds = Buffer.from(`${username}:${apiKey}`, 'binary').toString(
'base64',
);
const creds = Buffer.from(
`${instanceConfig.username}:${instanceConfig.apiKey}`,
'binary',
).toString('base64');
return {
baseUrl,
baseUrl: instanceConfig.baseUrl,
headers: {
Authorization: `Basic ${creds}`,
},
@@ -128,48 +141,92 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider {
private static getInstanceConfig(
jenkinsName: string | undefined,
rootConfig: Config,
): Config {
config: {
name: string;
baseUrl: string;
username: string;
apiKey: string;
}[],
): { name: string; baseUrl: string; username: string; apiKey: string } {
const DEFAULT_JENKINS_NAME = 'default';
const jenkinsConfig = rootConfig.getConfig('jenkins');
if (!jenkinsName || jenkinsName === DEFAULT_JENKINS_NAME) {
// no name provided, this could be
// (jenkins.baseUrl, jenkins.username, jenkins.apiKey) or
// the entry with default name in jenkins.instances
const namedInstanceConfig = jenkinsConfig
.getOptionalConfigArray('instances')
?.filter(c => c.getString('name') === DEFAULT_JENKINS_NAME)[0];
if (namedInstanceConfig) {
return namedInstanceConfig;
}
// no name provided, use default
const instanceConfig = config.find(c => c.name === DEFAULT_JENKINS_NAME);
// Get these as optional strings and check to give a better error message
const baseUrl = jenkinsConfig.getOptionalString('baseUrl');
const username = jenkinsConfig.getOptionalString('username');
const apiKey = jenkinsConfig.getOptionalString('apiKey');
if (!baseUrl || !username || !apiKey) {
if (!instanceConfig) {
throw new Error(
`Couldn't find a default jenkins instance in the config. Either configure an instance with name ${DEFAULT_JENKINS_NAME} or add a prefix to your annotation value.`,
);
}
return jenkinsConfig;
return instanceConfig;
}
// A name is provided, look it up.
const instanceConfig = config.find(c => c.name === jenkinsName);
const namedInstanceConfig = jenkinsConfig
.getConfigArray('instances')
.filter(c => c.getString('name') === jenkinsName)[0];
if (!namedInstanceConfig) {
if (!instanceConfig) {
throw new Error(
`Couldn't find a jenkins instance in the config with name ${jenkinsName}`,
);
}
return instanceConfig;
}
private static loadConfig(
rootConfig: Config,
): { name: string; baseUrl: string; username: string; apiKey: string }[] {
const DEFAULT_JENKINS_NAME = 'default';
const jenkinsConfig = rootConfig.getConfig('jenkins');
// load all named instance config
const namedInstanceConfig =
jenkinsConfig.getOptionalConfigArray('instances')?.map(c => ({
name: c.getString('name'),
baseUrl: c.getString('baseUrl'),
username: c.getString('username'),
apiKey: c.getString('apiKey'),
})) || [];
// load unnamed default config
const hasNamedDefault = namedInstanceConfig.some(
x => x.name === DEFAULT_JENKINS_NAME,
);
// Get these as optional strings and check to give a better error message
const baseUrl = jenkinsConfig.getOptionalString('baseUrl');
const username = jenkinsConfig.getOptionalString('username');
const apiKey = jenkinsConfig.getOptionalString('apiKey');
if (hasNamedDefault && (baseUrl || username || apiKey)) {
throw new Error(
`Found both a named jenkins instance with name ${DEFAULT_JENKINS_NAME} and top level baseUrl, username or apiKey config. Use only one style of config.`,
);
}
const unnamedNonePresent = !baseUrl && !username && !apiKey;
const unnamedAllPresent = baseUrl && username && apiKey;
if (!(unnamedAllPresent || unnamedNonePresent)) {
throw new Error(
`Found partial default jenkins config. All (or none) of baseUrl, username ans apiKey must be provided.`,
);
}
if (unnamedAllPresent) {
const unnamedInstanceConfig = [
{ name: DEFAULT_JENKINS_NAME, baseUrl, username, apiKey },
] as {
name: string;
baseUrl: string;
username: string;
apiKey: string;
}[];
return [...namedInstanceConfig, ...unnamedInstanceConfig];
}
return namedInstanceConfig;
}
}