Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build Task UI #4322

Merged
merged 1 commit into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .cloudgov/vars/pages-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ uaa_login_host: https://login.fr-stage.cloud.gov
new_relic_app_name: web-pages-dev
proxy_domain: sites.pages-dev.cloud.gov
cf_cdn_space_name: sites-dev
node_env: development
node_env: development
2 changes: 1 addition & 1 deletion .cloudgov/vars/pages-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ uaa_login_host: https://login.fr-stage.cloud.gov
new_relic_app_name: web-pages-staging
proxy_domain: sites.pages-staging.cloud.gov
cf_cdn_space_name: sites-staging
node_env: production
node_env: production
12 changes: 12 additions & 0 deletions api/controllers/build-task-type.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const { wrapHandlers } = require('../utils');
const { BuildTaskType } = require('../models');

module.exports = wrapHandlers({
list: async (req, res) => {
const taskTypes = await BuildTaskType.findAll({
attributes: { exclude: ['metadata', 'createdAt', 'updatedAt', 'runner', 'url'] },
});

return res.json(taskTypes);
},
});
19 changes: 16 additions & 3 deletions api/controllers/build-task.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { wrapHandlers } = require('../utils');
const { Build, BuildTask } = require('../models');
const { Build, BuildTask, BuildTaskType } = require('../models');
const { getSignedTaskUrl, getTaskArtifactSize } = require('../services/S3BuildTask');

module.exports = wrapHandlers({
find: async (req, res) => {
Expand All @@ -16,6 +17,7 @@ module.exports = wrapHandlers({
const task = await BuildTask.findOne({
where: { buildId, id: buildTaskId },
attributes: { exclude: ['token', 'deletedAt'] },
include: BuildTaskType,
});

if (!task) {
Expand All @@ -37,10 +39,21 @@ module.exports = wrapHandlers({

const tasks = await BuildTask.findAll({
where: { buildId },
attributes: { exclude: ['token', 'deletedAt'] },
attributes: { exclude: ['token', 'deletedAt', 'name'] },
include: BuildTaskType,
});

return res.json(tasks);
const updatedTasks = await Promise.all(tasks.map(async (task) => {
if (task.artifact) {
const size = await getTaskArtifactSize(build.Site, task.artifact);
const url = await getSignedTaskUrl(build.Site, task.artifact);
// eslint-disable-next-line no-param-reassign
task.artifact = { size, url };
}
return task;
}));

return res.json(updatedTasks);
},

update: async (req, res) => {
Expand Down
13 changes: 13 additions & 0 deletions api/controllers/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const emitBuildStatus = async (build) => {
module.exports = wrapHandlers({
async find(req, res) {
const site = await fetchModelById(req.params.site_id, Site);

if (!site) {
return res.notFound();
}
Expand Down Expand Up @@ -68,6 +69,18 @@ module.exports = wrapHandlers({
*
* e.g. `sites/1/builds/1`
*/

async findById(req, res) {
const { user, params } = req;
const { id } = params;
const build = await Build.forSiteUser(user).findByPk(id);
if (!build) {
return res.notFound();
}
const buildJSON = await buildSerializer.serialize(build);
return res.json(buildJSON);
},

async create(req, res) {
await siteAuthorizer.createBuild(req.user, { id: req.body.siteId });
const requestBuild = await Build.findOne({
Expand Down
3 changes: 3 additions & 0 deletions api/models/build-task-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ module.exports = (sequelize, DataTypes) => {
isIn: [StartsWhens.values],
},
},
url: {
apburnes marked this conversation as resolved.
Show resolved Hide resolved
type: DataTypes.STRING,
},
}, {
tableName: 'build_task_type',
}
Expand Down
18 changes: 14 additions & 4 deletions api/models/build-task.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ const associate = ({ BuildTask, Build, BuildTaskType }) => {
where: {
'$Build.site$': id,
},
include: [{
model: Build,
}],
include: [
{
model: Build,
},
BuildTaskType,
],
}));
};

Expand Down Expand Up @@ -63,7 +66,14 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.STRING,
allowNull: false,
},
}, {
message: {
type: DataTypes.STRING,
},
count: {
type: DataTypes.INTEGER,
},
},
{
tableName: 'build_task',
paranoid: true,
indexes: [
Expand Down
2 changes: 2 additions & 0 deletions api/routers/build-task.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
const router = require('express').Router();
const BuildTaskController = require('../controllers/build-task');
const BuildTaskTypeController = require('../controllers/build-task-type');
const { sessionAuth } = require('../middlewares');

router.get('/build/:build_id/tasks', sessionAuth, BuildTaskController.list);
router.get('/build/:build_id/tasks/:build_task_id', sessionAuth, BuildTaskController.find);
router.put('/tasks/:build_task_id/:token', BuildTaskController.update);
router.get('/tasks/types', BuildTaskTypeController.list);

module.exports = router;
1 change: 1 addition & 0 deletions api/routers/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const BuildController = require('../controllers/build');
const { csrfProtection, sessionAuth } = require('../middlewares');

router.get('/site/:site_id/build', sessionAuth, BuildController.find);
router.get('/build/:id', sessionAuth, BuildController.findById);
router.post('/build', sessionAuth, csrfProtection, BuildController.create);
router.post('/build/:id/status/:token', BuildController.status);

Expand Down
7 changes: 6 additions & 1 deletion api/serializers/build.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const {
Build, Domain, Site, SiteBranchConfig, User,
Build, Domain, Site, SiteBranchConfig, User, BuildTask, BuildTaskType,
} = require('../models');
const { buildViewLink } = require('../utils/build');
const siteSerializer = require('./site');
Expand Down Expand Up @@ -62,6 +62,11 @@ const serialize = async (serializable) => {
required: true,
include: [SiteBranchConfig, Domain],
},
{
model: BuildTask,
required: false,
include: [BuildTaskType],
},
],
});

Expand Down
52 changes: 52 additions & 0 deletions api/services/S3BuildTask.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const S3Helper = require('./S3Helper');
const CloudFoundryAPIClient = require('../utils/cfApiClient');

const apiClient = new CloudFoundryAPIClient();

const handleInvalidAccessKeyError = (error) => {
const validS3KeyUpdateEnv = process.env.NODE_ENV === 'development'
|| process.env.NODE_ENV === 'test';

if (error.code === 'InvalidAccessKeyId' && validS3KeyUpdateEnv) {
const message = 'S3 keys out of date. Update them with `npm run update-local-config`';
throw {
message,
status: 400,
};
}

throw error;
};

async function getTaskArtifactSize(site, key) {
return apiClient.fetchServiceInstanceCredentials(site.s3ServiceName)
.then((credentials) => {
const s3Client = new S3Helper.S3Client({
accessKeyId: credentials.access_key_id,
secretAccessKey: credentials.secret_access_key,
region: credentials.region,
bucket: credentials.bucket,
});

return s3Client.headObject(key)
.then(metadata => metadata.ContentLength)
.catch(handleInvalidAccessKeyError);
});
}

async function getSignedTaskUrl(site, key) {
return apiClient.fetchServiceInstanceCredentials(site.s3ServiceName)
.then((credentials) => {
const s3Client = new S3Helper.S3Client({
accessKeyId: credentials.access_key_id,
secretAccessKey: credentials.secret_access_key,
region: credentials.region,
bucket: credentials.bucket,
});

return s3Client.getSignedUrl(key)
.catch(handleInvalidAccessKeyError);
});
}

module.exports = { getTaskArtifactSize, getSignedTaskUrl };
21 changes: 21 additions & 0 deletions api/services/S3Helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ const {
GetObjectCommand,
waitUntilBucketExists,
DeleteObjectsCommand,
HeadObjectCommand,
} = require('@aws-sdk/client-s3');

const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');

const S3_DEFAULT_MAX_KEYS = 1000;

class S3Client {
Expand Down Expand Up @@ -153,6 +156,24 @@ class S3Client {
});
return client.send(command);
}

async headObject(key) {
const { bucket, client } = this;
const command = new HeadObjectCommand({
Bucket: bucket,
Key: key,
});
return client.send(command);
}

async getSignedUrl(key) {
const { bucket, client } = this;
const command = new GetObjectCommand({
Bucket: bucket,
Key: key,
});
return getSignedUrl(client, command, { expiresIn: 300 });
}
}

module.exports = { S3_DEFAULT_MAX_KEYS, S3Client };
9 changes: 9 additions & 0 deletions ci/pipeline-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
plan:
- get: src
resource: pr-((deploy-env))
params: {integration_tool: checkout}
apburnes marked this conversation as resolved.
Show resolved Hide resolved
passed: [set-pipeline]
trigger: true
- put: gh-status
Expand Down Expand Up @@ -68,6 +69,7 @@ jobs:
APP_HOSTNAME: https://((((deploy-env))-pages-domain))
NODE_ENV: development
PROXY_DOMAIN: sites.((((deploy-env))-pages-domain))
FEATURE_BUILD_TASKS: true

- task: deploy-api
file: src/ci/partials/deploy.yml
Expand Down Expand Up @@ -141,6 +143,7 @@ jobs:
plan:
- get: src
resource: pr-((deploy-env))
params: {integration_tool: checkout}
passed: [set-pipeline]
trigger: true
- put: gh-status
Expand Down Expand Up @@ -205,6 +208,7 @@ jobs:
plan:
- get: src
resource: pr-((deploy-env))
params: {integration_tool: checkout}
passed: [set-pipeline]
trigger: true
- put: gh-status
Expand Down Expand Up @@ -259,6 +263,7 @@ jobs:
plan:
- get: src
resource: pr-((deploy-env))
params: {integration_tool: checkout}
passed: [set-pipeline]
- get: cf-image
- get: nightly
Expand All @@ -281,6 +286,7 @@ jobs:
plan:
- get: src
resource: pr-((deploy-env))
params: {integration_tool: checkout}
trigger: true
passed:
- test-and-deploy-api-pages
Expand Down Expand Up @@ -346,6 +352,7 @@ jobs:
plan:
- get: src
resource: pr-((deploy-env))
params: {integration_tool: checkout}
trigger: true
passed: [set-pipeline]
- get: node
Expand Down Expand Up @@ -387,6 +394,7 @@ jobs:
plan:
- get: src
resource: pr-((deploy-env))
params: {integration_tool: checkout}
passed: [set-pipeline]
- get: nightly
trigger: true
Expand Down Expand Up @@ -432,6 +440,7 @@ jobs:
plan:
- get: src
resource: pr-((deploy-env))
params: {integration_tool: checkout}
trigger: true
- set_pipeline: core
file: src/ci/pipeline-dev.yml
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ services:
DOMAIN: localhost:1337
FEATURE_AUTH_GITHUB: 'true'
FEATURE_AUTH_UAA: 'true'
FEATURE_BUILD_TASKS: 'true'
PORT: 1337
PRODUCT: pages
UAA_HOST: http://localhost:9000
Expand Down
13 changes: 13 additions & 0 deletions frontend/actions/actionCreators/buildActions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const buildsFetchStartedType = 'BUILDS_FETCH_STARTED';
const buildsReceivedType = 'BUILDS_RECEIVED';
const buildFetchStartedType = 'BUILD_FETCH_STARTED';
const buildReceivedType = 'BUILD_RECEIVED';
const buildRestartedType = 'BUILD_RESTARTED';

const buildsFetchStarted = () => ({
Expand All @@ -11,6 +13,15 @@ const buildsReceived = builds => ({
builds,
});

const buildFetchStarted = () => ({
type: buildFetchStartedType,
});

const buildReceived = build => ({
type: buildReceivedType,
build,
});

const buildRestarted = build => ({
type: buildRestartedType,
build,
Expand All @@ -19,5 +30,7 @@ const buildRestarted = build => ({
export {
buildsFetchStarted, buildsFetchStartedType,
buildsReceived, buildsReceivedType,
buildFetchStarted, buildFetchStartedType,
buildReceived, buildReceivedType,
buildRestarted, buildRestartedType,
};
Loading
Loading