feat(scaffolder): Moving the storage to more task based appraches, and creating preparers
This commit is contained in:
@@ -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';
|
||||
|
||||
+2
-9
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user