feat(scaffolder): Moving the storage to more task based appraches, and creating preparers

This commit is contained in:
blam
2020-06-17 11:56:42 +02:00
parent bbef36c91a
commit 55e6414d85
8 changed files with 126 additions and 26 deletions
+1 -1
View File
@@ -48,7 +48,7 @@ export type Entity = {
* @see https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#objectmeta-v1-meta
* @see https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/
*/
export type EntityMeta = {
export type EntityMeta = Record<string, any> & {
/**
* A globally unique ID for the entity.
*
@@ -26,6 +26,7 @@ export interface TemplateEntityV1alpha1 extends Entity {
kind: typeof KIND;
spec: {
type: string;
path?: string;
};
}
@@ -39,6 +40,7 @@ export class TemplateEntityV1alpha1Policy implements EntityPolicy {
spec: yup
.object({
type: yup.string().required().min(1),
path: yup.string(),
})
.required(),
});
@@ -14,5 +14,5 @@
* limitations under the License.
*/
export * from './templater';
export * from './prepare';
export * from './templater/cookiecutter';
@@ -13,12 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { TemplateEntityV1alpha1 } from '@backstage/catalog-model';
export type StorageBase = {
/**
*
* @param id
*/
prepare(template: TemplateEntityV1alpha1): Promise<string>;
};
export * from './preparers';
export * from './types';
@@ -0,0 +1,51 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { PreparerBase, RemoteLocation, PreparerBuilder } from './types';
import { TemplateEntityV1alpha1 } from '@backstage/catalog-model';
export class Preparers implements PreparerBuilder {
private preparerMap = new Map<RemoteLocation, PreparerBase>();
register(key: RemoteLocation, processor: PreparerBase) {
this.preparerMap.set(key, processor);
}
get(template: TemplateEntityV1alpha1): PreparerBase {
const preparerKey = this.getPreparerKeyFromEntity(template);
const preparer = this.preparerMap.get(preparerKey);
if (!preparer) {
throw new Error(`No preparer registered for type ${preparerKey}`);
}
return preparer;
}
private getPreparerKeyFromEntity(
entity: TemplateEntityV1alpha1,
): RemoteLocation {
const annotation =
entity.metadata.annotations?.['backstage.io/managed-by-location'] ?? '';
const [key] = annotation?.split(':');
if (!key) {
throw new Error('Failed to parse the location data');
}
return key as RemoteLocation;
}
}
@@ -0,0 +1,32 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { TemplateEntityV1alpha1 } from '@backstage/catalog-model';
export type PreparerBase = {
/**
* Given an Entity definition from the Service Catalog, go and prepare a directory
* with contents from the remote location in temporary storage and return the path
* @param template The template entity from the Service Catalog
*/
prepare(template: TemplateEntityV1alpha1): Promise<string>;
};
export type PreparerBuilder = {
register(key: RemoteLocation, preparer: PreparerBase): void;
get(key: TemplateEntityV1alpha1): PreparerBase;
};
export type RemoteLocation = 'file';
@@ -17,10 +17,11 @@
import { Logger } from 'winston';
import Router from 'express-promise-router';
import express from 'express';
import { StorageBase, TemplaterBase } from '../scaffolder';
import { PreparerBuilder, TemplaterBase } from '../scaffolder';
import { TemplateEntityV1alpha1 } from '@backstage/catalog-model';
export interface RouterOptions {
storage: StorageBase;
preparers: PreparerBuilder;
templater: TemplaterBase;
logger: Logger;
}
@@ -29,22 +30,43 @@ export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const router = Router();
const { storage, templater, logger: parentLogger } = options;
const { preparers, templater, logger: parentLogger } = options;
const logger = parentLogger.child({ plugin: 'scaffolder' });
router
.get('/v1/templates', async (_, res) => {
const templates = await storage.list();
res.status(200).json(templates);
})
.post('/v1/jobs', async (_, res) => {
// TODO(blam): Actually make this function work
const mock = 'templateid';
res.status(201).json({ accepted: true });
router.post('/v1/jobs', async (_, res) => {
// TODO(blam): Actually make this function work
// res.status(201).json({ accepted: true });
const path = await storage.prepare(mock);
await templater.run({ directory: path, values: { componentId: 'test' } });
});
const mockEntity: TemplateEntityV1alpha1 = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Template',
metadata: {
annotations: {
'backstage.io/managed-by-location':
'file:/Users/blam/dev/spotify/backstage/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml',
},
name: 'react-ssr-template',
title: 'React SSR Template',
description:
'Next.js application skeleton for creating isomorphic web applications.',
uid: '7357f4c5-aa58-4a1e-9670-18931eef771f',
etag: 'YWUxZWQyY2EtZDkxMC00MDM0LWI0ODAtMDgwMWY0YzdlMWIw',
generation: 1,
},
spec: {
type: 'cookiecutter',
path: '.',
},
};
// fetch the entity from service catalog here.
const preparer = preparers.get(mockEntity);
//
const path = await preparer.prepare(mockEntity);
await templater.run({ directory: path, values: { componentId: 'test' } });
});
const app = express();
app.set('logger', logger);