Skip to content

Commit

Permalink
Add enablement of Partner Managed ISVs (#52)
Browse files Browse the repository at this point in the history
* Add enablement of Partner Managed ISVs

* update permissions for dev installed user
  • Loading branch information
jeff-phillips-18 authored Apr 20, 2021
1 parent afed0f4 commit 4ce1fb0
Show file tree
Hide file tree
Showing 25 changed files with 458 additions and 48 deletions.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ endif

##################################

# DEV Convenience

reinstall: build push undeploy deploy

##################################

# DEV - run apps locally for development

.PHONY: dev-frontend
Expand Down
6 changes: 6 additions & 0 deletions backend/plugins/kube.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ kc.loadFromDefault();

const currentContext = kc.getCurrentContext();
const customObjectsApi = kc.makeApiClient(k8s.CustomObjectsApi);
const coreV1Api = kc.makeApiClient(k8s.CoreV1Api);
const batchV1Api = kc.makeApiClient(k8s.BatchV1Api);
const batchV1beta1Api = kc.makeApiClient(k8s.BatchV1beta1Api);
const currentUser = kc.getCurrentUser();

module.exports = fp(async (fastify) => {
Expand All @@ -24,6 +27,9 @@ module.exports = fp(async (fastify) => {
config: kc,
currentContext,
namespace,
coreV1Api,
batchV1beta1Api,
batchV1Api,
customObjectsApi,
currentUser,
});
Expand Down
40 changes: 38 additions & 2 deletions backend/routes/api/components/componentUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,45 @@ const getInstalledOperators = async (fastify) => {
csvs = _.get(res, 'body.items');
} catch (e) {
fastify.log.error(e, 'failed to get ClusterServiceVersions');
csvs = [];
}

return csvs.reduce((acc, csv) => {
if (csv.status.phase === 'Succeeded' && csv.status.reason === 'InstallSucceeded') {
if (csv.status?.phase === 'Succeeded' && csv.status?.reason === 'InstallSucceeded') {
acc.push(csv);
}
return acc;
}, []);
};

const getApplicationEnabledConfigMap = (fastify, appDef) => {
const namespace = fastify.kube.namespace;
const name = appDef.spec.enable?.validationConfigMap;
if (!name) {
Promise.resolve(null);
}
const coreV1Api = fastify.kube.coreV1Api;
return coreV1Api
.readNamespacedConfigMap(name, namespace)
.then((result) => result.body)
.catch((res) => {
fastify.log.error(
`Failed to read config map ${name} for ${appDef.metadata.name}: ${res.response?.body?.message}`,
);
Promise.resolve(null);
});
};

const getEnabledConfigMaps = (fastify, appDefs) => {
const configMapGetters = appDefs.reduce((acc, app) => {
if (app.spec.enable) {
acc.push(getApplicationEnabledConfigMap(fastify, app));
}
return acc;
}, []);
return Promise.all(configMapGetters);
};

const getApplicationDefs = () => {
const normalizedPath = path.join(__dirname, '../../../../data/applications');
const applicationDefs = [];
Expand All @@ -93,4 +122,11 @@ const getApplicationDefs = () => {
return applicationDefs;
};

module.exports = { getInstalledKfdefs, getInstalledOperators, getLink, getApplicationDefs };
module.exports = {
getInstalledKfdefs,
getInstalledOperators,
getLink,
getApplicationEnabledConfigMap,
getEnabledConfigMaps,
getApplicationDefs,
};
28 changes: 23 additions & 5 deletions backend/routes/api/components/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ const componentUtils = require('./componentUtils');
module.exports = async ({ fastify, request }) => {
const applicationDefs = componentUtils.getApplicationDefs();

if (!request.query.installed) {
return await Promise.all(applicationDefs);
}

// Fetch the installed kfDefs
const kfdefApps = await componentUtils.getInstalledKfdefs(fastify);

// Fetch the installed kfDefs
const operatorCSVs = await componentUtils.getInstalledOperators(fastify);

// Fetch the enabled config maps
const enabledCMs = await componentUtils.getEnabledConfigMaps(fastify, applicationDefs);
const getCSVForApp = (app) =>
operatorCSVs.find(
(operator) => app.spec.csvName && operator.metadata.name.startsWith(app.spec.csvName),
Expand All @@ -21,6 +19,7 @@ module.exports = async ({ fastify, request }) => {
// Get the components associated with the installed KfDefs or operators
const installedComponents = applicationDefs.reduce((acc, app) => {
if (getCSVForApp(app)) {
app.spec.isEnabled = true;
acc.push(app);
return acc;
}
Expand All @@ -29,14 +28,27 @@ module.exports = async ({ fastify, request }) => {
app.spec.kfdefApplications &&
kfdefApps.find((kfdefApp) => app.spec.kfdefApplications.includes(kfdefApp.name))
) {
app.spec.isEnabled = true;
acc.push(app);
return acc;
}

if (app.spec.enable) {
const cm = enabledCMs.find(
(enabledCM) => enabledCM?.metadata.name === app.spec.enable?.validationConfigMap,
);
if (cm) {
if (cm.data?.validation_result === 'true') {
app.spec.isEnabled = true;
acc.push(app);
return acc;
}
}
}
return acc;
}, []);

return await Promise.all(
await Promise.all(
installedComponents.map(async (installedComponent) => {
if (installedComponent.spec.route) {
const csv = getCSVForApp(installedComponent);
Expand All @@ -57,4 +69,10 @@ module.exports = async ({ fastify, request }) => {
return installedComponent;
}),
);

if (!request.query.installed) {
return await Promise.all(applicationDefs);
}

return await Promise.all(installedComponents);
};
23 changes: 23 additions & 0 deletions backend/routes/api/validate-isv/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const { DEV_MODE } = require('../../../utils/constants');
const responseUtils = require('../../../utils/responseUtils');
const validateISV = require('./validateISV');

module.exports = async (fastify, opts) => {
fastify.get('/', async (request, reply) => {
return validateISV
.createValidationJob({ fastify, opts, request, reply })
.then((res) => {
if (DEV_MODE) {
responseUtils.addCORSHeader(request, reply);
}
return res;
})
.catch((res) => {
if (DEV_MODE) {
responseUtils.addCORSHeader(request, reply);
}
fastify.log.error(`Failed to create validation job: ${res.response?.body?.message}`);
reply.send(res);
});
});
};
85 changes: 85 additions & 0 deletions backend/routes/api/validate-isv/validateISV.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
const componentUtils = require('../components/componentUtils');
const createError = require('http-errors');

const getApplicationDef = (appName) => {
const appDefs = componentUtils.getApplicationDefs();
return appDefs.find((appDef) => appDef.metadata.name === appName);
};

const createAccessSecret = async (appDef, namespace, stringData, coreV1Api) => {
const { enable } = appDef.spec;
if (!enable) {
return Promise.resolve();
}

stringData.configMapName = enable.validationConfigMap;
const name = enable.validationSecret;
const secret = {
apiVersion: 'v1',
metadata: { name, namespace },
type: 'Opaque',
stringData,
};
return coreV1Api
.readNamespacedSecret(name, namespace)
.then(() => {
return coreV1Api.replaceNamespacedSecret(name, namespace, secret);
})
.catch(() => {
return coreV1Api.createNamespacedSecret(namespace, secret);
});
};

const createValidationJob = async ({ fastify, request }) => {
const namespace = fastify.kube.namespace;
const appName = request.query?.appName;
const stringData = JSON.parse(request.query?.values ?? {});
const batchV1beta1Api = fastify.kube.batchV1beta1Api;
const batchV1Api = fastify.kube.batchV1Api;
const coreV1Api = fastify.kube.coreV1Api;
const appDef = getApplicationDef(appName);
const { enable } = appDef.spec;

const cronjobName = enable?.validationJob;
if (!cronjobName) {
const error = createError(500, 'failed to validate');
error.explicitInternalServerError = true;
error.error = 'failed to find application definition file';
error.message = 'Unable to validate the application.';
throw error;
}

return createAccessSecret(appDef, namespace, stringData, coreV1Api).then(() => {
return batchV1beta1Api.readNamespacedCronJob(cronjobName, namespace).then(async (res) => {
const cronJob = res.body;
const jobSpec = cronJob.spec.jobTemplate.spec;
const jobName = `${cronjobName}-job-custom-run`;
const job = {
apiVersion: 'batch/v1',
metadata: {
name: jobName,
namespace,
annotations: {
'cronjob.kubernetes.io/instantiate': 'manual',
},
},
spec: jobSpec,
};
// Flag the cronjob as no longer suspended
cronJob.spec.suspend = false;
await batchV1beta1Api.replaceNamespacedCronJob(cronjobName, namespace, cronJob).catch((e) => {
fastify.log.error(`failed to unsuspend cronjob: ${e.response.body.message}`);
});

// If there was a manual job already, delete it
await batchV1Api.deleteNamespacedJob(jobName, namespace).catch(() => {});

// Some delay to allow job to delete
return new Promise((resolve) => setTimeout(resolve, 1000)).then(() =>
batchV1Api.createNamespacedJob(namespace, job),
);
});
});
};

module.exports = { createAccessSecret, createValidationJob };
18 changes: 15 additions & 3 deletions data/applications/anaconda-ce.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
metadata:
name: anaconde-ce
name: anaconda-ce
spec:
displayName: Anaconda Commercial Edition
provider: Anaconda
Expand All @@ -13,5 +13,17 @@ spec:
support: third party support
docsLink: https://docs.anaconda.com/
quickStart: ''
getStartedLink: https://anaconda.cloud/register?utm_source=redhat-rhods-summit

getStartedLink: https://anaconda.cloud/register?utm_source=redhat-rhods-summit
enable:
title: Connect Anaconda to JupyterHub
actionLabel: Connect
description: ''
variables:
Anaconda_ce_key: password
variableDisplayText:
Anaconda_ce_key: Anaconda CE Key
variableHelpText:
Anaconda_ce_key: This key is given to you by Anaconda
validationJob: anaconda-ce-periodic-validator
validationSecret: anaconda-ce-access
validationConfigMap: anaconda-ce-validation-result
2 changes: 1 addition & 1 deletion data/docs/anaconda-tutorial.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ metadata:
name: anaconda-background
type: tutorial
spec:
appName: anaconde-ce
appName: anaconda-ce
displayName: What is Anaconda?
description: |-
Learn about the Anaconda Distrbution and why Python is so frequently used for Data Science tasks.
Expand Down
2 changes: 1 addition & 1 deletion data/docs/python-acceleration-numba-tutorial.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ metadata:
name: python-accelerate-numba-tutorial
type: tutorial
spec:
appName: anaconde-ce
appName: anaconda-ce
displayName: Accelerating Scientific Workloads in Python with Numba
description: |-
If you're interesting making your Python code run faster, this talk is for you
Expand Down
2 changes: 1 addition & 1 deletion data/docs/python-effective-pandas-tutorial.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ metadata:
name: python-effective-pandas-tutorial
type: tutorial
spec:
appName: anaconde-ce
appName: anaconda-ce
displayName: Data analysis in Python with Pandas
description: |-
This series is about how to make effective use of pandas, a data analysis library for the Python programming language. It's targeted at an intermediate level: people who have some experience with pandas, but are looking to improve.
Expand Down
2 changes: 1 addition & 1 deletion data/docs/python-gpu-numba-tutorial.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ metadata:
name: python-gpu-numba-tutorial
type: tutorial
spec:
appName: anaconde-ce
appName: anaconda-ce
displayName: GPU Computing in Python with Numba
description: |-
Learn how to use Numba to create GPU accelerated functions.
Expand Down
2 changes: 1 addition & 1 deletion data/docs/python-interactive-visualization-tutorial.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ metadata:
name: python-interactive-visualization-tutorial
type: tutorial
spec:
appName: anaconde-ce
appName: anaconda-ce
displayName: Build interactive visualizations and dashboards in Python
description: |-
This tutorial will take you through all of the steps involved in exploring data of many different types and sizes, building simple and complex figures, working with billions of data points, adding interactive behavior, widgets and controls, and deploying full dashboards and applications.
Expand Down
2 changes: 1 addition & 1 deletion data/docs/python-scalable-computing-dask-tutorial.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ metadata:
name: python-scalable-computing-dask-tutorial
type: tutorial
spec:
appName: anaconde-ce
appName: anaconda-ce
displayName: Scalable computing in Python with Dask
description: |-
Dask is a parallel computing library that scales the existing Python ecosystem. This tutorial will introduce Dask and parallel data analysis more generally. Dask can scale down to your laptop and up to a cluster. In this tutorial, we’ll analyze medium sized datasets in parallel.
Expand Down
2 changes: 1 addition & 1 deletion data/docs/python-scikit-learn-tutorial.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ metadata:
name: python-scikit-learn-tutorial
type: tutorial
spec:
appName: anaconde-ce
appName: anaconda-ce
displayName: Machine Learning in Python with Scikit-Learn
description: |-
Learn how to build machine learning models with scikit-learn for supervised learning, unsupervised learning, and classification problems.
Expand Down
2 changes: 1 addition & 1 deletion data/docs/python-visualization-tutorial.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ metadata:
name: python-visualization-tutorial
type: tutorial
spec:
appName: anaconde-ce
appName: anaconda-ce
displayName: Python tools for data visualization
description: |-
The PyViz.org website is an open platform for helping users decide on the best open-source (OSS) Python data visualization tools for their purposes, with links, overviews, comparisons, and examples.
Expand Down
File renamed without changes.
Loading

0 comments on commit 4ce1fb0

Please sign in to comment.