Skip to content

Commit

Permalink
feat: Knative Serving Deployment (#454)
Browse files Browse the repository at this point in the history
* This adds the experimental flag `--knative`.    The purpose of this flag is to make deploying a Knative Serving Service easy.  Since this is experimental,  it can change without a major version bump

* For a deploy, the first part(build) is unchanged, the "apply-resource" part, which is the second phase of a nodeshift deploy, has been updated to only create a knative serving service that points to the image that was just built in the first step.

* If an application has a .nodeshift directory with resource files that are not related to knative, they are ignored
  • Loading branch information
lholmquist committed Jun 5, 2020
1 parent fca4ad8 commit 88eed5d
Show file tree
Hide file tree
Showing 20 changed files with 1,960 additions and 1,733 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ Flag to pass build config environment variables as NAME=Value. Can be used mult
#### build.strategy
Flag to change the build strategy used. Values can be Docker or Source. Defaults to Source
#### knative
EXPERIMENTAL. Flag to deploy an application as a Knative Serving Service. Defaults to false
Since this feature is experimental, it is subject to change without a Major version release until it is fully stable.
#### help
Shows the below help
Expand Down Expand Up @@ -254,6 +258,9 @@ Shows the below help
--metadata.out determines what should be done with the response
metadata from OpenShift
[string] [choices: "stdout", "ignore", "<filename>"] [default: "ignore"]
--knative EXPERIMENTAL. flag to deploy an application
as a Knative Serving Service
[boolean] [choices: true, false] [default: false]
--help Show help [boolean]
--cmd [default: "deploy"]
Expand Down
8 changes: 8 additions & 0 deletions bin/nodeshift
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ yargs
type: 'string',
default: 'ignore'
})
.options('knative', {
describe: 'EXPERIMENTAL. flag to deploy an application as a Knative Serving Service',
choices: [true, false],
type: 'boolean',
default: false
})
.argv;

function commandHandler (argv) {
Expand Down Expand Up @@ -162,6 +168,8 @@ function commandHandler (argv) {
function createOptions (argv) {
const options = {};

options.knative = argv.knative === true || argv.knative === 'true';

options.projectLocation = argv.projectLocation;
options.dockerImage = argv.dockerImage;
options.imageTag = argv.imageTag;
Expand Down
4 changes: 4 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const cli = require('./bin/cli');
@param {boolean} [options.build.forcePull] - flag to make your BuildConfig always pull a new image from dockerhub or not. Defaults to false
@param {Array} [options.build.env] - an array of objects to pass build config environment variables. [{name: NAME_PROP, value: VALUE}]
@param {array} [options.definedProperties] - Array of objects with the format { key: value }. Used for template substitution
@param {boolean} [options.knative] - EXPERIMENTAL. flag to deploy an application as a Knative Serving Service. Defaults to false
@returns {Promise<object>} - Returns a JSON Object
*/
function deploy (options = {}) {
Expand All @@ -55,6 +56,7 @@ function deploy (options = {}) {
@param {string/boolean} [options.build.recreate] - flag to recreate a buildConfig or Imagestream. values are "buildConfig", "imageStream", true, false. Defaults to false
@param {boolean} [options.build.forcePull] - flag to make your BuildConfig always pull a new image from dockerhub or not. Defaults to false
@param {array} [options.definedProperties] - Array of objects with the format { key: value }. Used for template substitution
@param {boolean} [options.knative] - EXPERIMENTAL. flag to deploy an application as a Knative Serving Service. Defaults to false
@returns {Promise<object>} - Returns a JSON Object
*/
function resource (options = {}) {
Expand Down Expand Up @@ -83,6 +85,7 @@ function resource (options = {}) {
@param {string/boolean} [options.build.recreate] - flag to recreate a buildConfig or Imagestream. values are "buildConfig", "imageStream", true, false. Defaults to false
@param {boolean} [options.build.forcePull] - flag to make your BuildConfig always pull a new image from dockerhub or not. Defaults to false
@param {array} [options.definedProperties] - Array of objects with the format { key: value }. Used for template substitution
@param {boolean} [options.knative] - EXPERIMENTAL. flag to deploy an application as a Knative Serving Service. Defaults to false
@returns {Promise<object>} - Returns a JSON Object
*/
function applyResource (options = {}) {
Expand Down Expand Up @@ -111,6 +114,7 @@ function applyResource (options = {}) {
@param {string/boolean} [options.build.recreate] - flag to recreate a buildConfig or Imagestream. values are "buildConfig", "imageStream", true, false. Defaults to false
@param {boolean} [options.build.forcePull] - flag to make your BuildConfig always pull a new image from dockerhub or not. Defaults to false
@param {array} [options.definedProperties] - Array of objects with the format { key: value }. Used for template substitution
@param {boolean} [options.knative] - EXPERIMENTAL. flag to deploy an application as a Knative Serving Service. Defaults to false
@returns {Promise<object>} - Returns a JSON Object
*/
function undeploy (options = {}) {
Expand Down
6 changes: 5 additions & 1 deletion lib/enrich-resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@

const loadEnrichers = require('./load-enrichers');
const defaultEnrichers = require('./resource-enrichers/default-enrichers.json');
const knativeEnrichers = require('./resource-enrichers/knative-enrichers.json');

// TODO: Add Knative Serving Enrichers to run instead?
// Split the defaults to be "normal" and "knative"?

module.exports = async (config, resourceList) => {
// Load a list of enrichers
Expand All @@ -28,7 +32,7 @@ module.exports = async (config, resourceList) => {
// Loop through those and then enrich the items from the resourceList
let enrichedList = resourceList;
// the defaultEnrichers list will have the correct order
for (const enricher of defaultEnrichers) {
for (const enricher of (config.knative ? knativeEnrichers : defaultEnrichers)) {
const fn = loadedEnrichers[enricher];
if (typeof fn === 'function') {
enrichedList = await fn(config, enrichedList);
Expand Down
4 changes: 4 additions & 0 deletions lib/goals/apply-resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
'use strict';

const services = require('../services');
const knativeServingService = require('../knative-serving-service');
const routes = require('../routes');
const deploymentConfig = require('../deployment-config').deploy;
const secrets = require('../secrets');
Expand All @@ -29,6 +30,9 @@ module.exports = (config, resourceList) => {
const mappedResources = resourceList.map((r) => {
switch (r.kind) {
case 'Service':
if (r.apiVersion.includes('serving.knative.dev')) {
return knativeServingService(config, r);
}
return services(config, r);
case 'Route':
return routes(config, r);
Expand Down
6 changes: 4 additions & 2 deletions lib/goals/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ const writeResources = require('../resource-writer');

module.exports = async function resources (config) {
// Load Resources. This looks at the .nodeshift directory and loads those files
const loadedResources = await resourceLoader(config);
// TODO: figure out how to load only the resource we want
const resourceToEnrich = await resourceLoader(config);

// "Enrich" them. This should add stuff that needs to be added
const enrichedResources = await enrichResources(config, loadedResources);
const enrichedResources = await enrichResources(config, resourceToEnrich);

// Write to file
await writeResources(config, enrichedResources);
Expand Down
6 changes: 5 additions & 1 deletion lib/goals/undeploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ module.exports = async function (config) {
response.route = await awaitRequest(config.openshiftRestClient.apis.route.v1.ns(config.namespace.name).routes(item.metadata.name).delete({ body: removeOptions }));
break;
case 'Service':
response.service = await awaitRequest(config.openshiftRestClient.api.v1.ns(config.namespace.name).service(item.metadata.name).delete({ body: removeOptions }));
if (item.apiVersion.includes('serving.knative.dev')) {
response.knativeServingService = await awaitRequest(config.openshiftRestClient.apis['serving.knative.dev'].v1.ns(config.namespace.name).service(item.metadata.name).delete({ body: removeOptions }));
} else {
response.service = await awaitRequest(config.openshiftRestClient.api.v1.ns(config.namespace.name).service(item.metadata.name).delete({ body: removeOptions }));
}
break;
case 'DeploymentConfig':
response.deploymentConfig = await undeploy(config, item);
Expand Down
35 changes: 35 additions & 0 deletions lib/knative-serving-service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
*
* Copyright 2016-2017 Red Hat, Inc, and individual contributors.
*
* 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.
*
*/

'use strict';

const log = require('./common-log')();
const { awaitRequest } = require('./helpers');

module.exports = async function getKnativeServingServices (config, resource) {
const knativeService = await awaitRequest(config.openshiftRestClient.apis['serving.knative.dev'].v1.ns(config.namespace.name).service(resource.metadata.name).get());

if (knativeService.code === 404) {
// There isn't a service yet, so we need to create one
log.info(`creating new service ${resource.metadata.name}`);
return config.openshiftRestClient.apis['serving.knative.dev'].v1.ns(config.namespace.name).service.post({ body: resource });
}

log.info(`using existing service ${knativeService.body.metadata.name}`);
return knativeService;
};
9 changes: 8 additions & 1 deletion lib/nodeshift-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,17 @@ async function setup (options = {}) {
options.nodeshiftDirectory = '.nodeshift';
options.projectLocation = options.projectLocation || process.cwd();

if (options.knative) {
logger.warning('Using the EXPERMINTAL knative flag.');
}

logger.info('loading configuration');
const projectPackage = JSON.parse(await readFile(`${options.projectLocation}/package.json`, { encoding: 'utf8' }));
// If there is a configLocation string, pass it in
const restClient = await openshiftRestClient({ config: options.configLocation });
const restClient = await openshiftRestClient({ config: options.configLocation, loadSpecFromCluster: options.knative });

// TODO(lholmquist): If knative is flagged, lets check that they have the API we need to use

const kubeConfig = restClient.kubeconfig;

const currentContext = kubeConfig.getCurrentContext();
Expand Down
3 changes: 3 additions & 0 deletions lib/resource-enrichers/knative-enrichers.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[
"knative-serving-service"
]
90 changes: 90 additions & 0 deletions lib/resource-enrichers/knative-service-enricher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
*
* Copyright 2016-2017 Red Hat, Inc, and individual contributors.
*
* 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.
*
*/

'use strict';

const _ = require('lodash');
const objectMetadata = require('../definitions/object-metadata');

const baseServiceConfig = {
apiVersion: 'serving.knative.dev/v1',
kind: 'Service',
spec: {} // Should be populated in the resource coming in
};

// Returns a default knative serving service.
function defaultService (config) {
const serviceConfig = _.merge({}, baseServiceConfig);

// Apply MetaData
serviceConfig.metadata = objectMetadata({
name: config.projectName,
namespace: config.namespace.name
});

// TODO(lholmquist): Should this also take into account docker images that are not in the openshift registry
serviceConfig.spec.template = {
spec: {
containers: [
{
image: `image-registry.openshift-image-registry.svc:5000/${config.namespace.name}/${config.projectName}`
}
]
}
};

return serviceConfig;
}

async function createKnativeServingService (config, resourceList) {
// First check to see if we have a Service
// TODO(lholmquist): Probably should check that this is a knative service not a normal service
// This might be a little tricky since we would have to check the apiVersion and at this point,
// we might not have it yet, there might be some updates needed in the resource loader.
// This should also be done in the service-enricher
if (_.filter(resourceList, { kind: 'Service' }).length < 1) {
// create the default service and add in to the resource list
resourceList.push(defaultService(config));
return resourceList;
}

// If there is one, then just do this
return resourceList.map((resource) => {
if (resource.kind !== 'Service') {
return resource;
}
// Merge the default Service Config with the current resource
return _.merge({}, defaultService(config), resource);
});
}

module.exports = {
enrich: createKnativeServingService,
name: 'knative-serving-service'
};

// apiVersion: serving.knative.dev/v1alpha1
// kind: Service
// metadata:
// name: helloworld-nodejs
// namespace: fun-times
// spec:
// template:
// spec:
// containers:
// - image: image-registry.openshift-image-registry.svc:5000/fun-times/nodejs-rest-http
17 changes: 15 additions & 2 deletions lib/resource-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ const kindMappings = {
secret: 'Secret',
deployment: 'Deployment',
ingress: 'Ingress',
config: 'ConfigMap'
config: 'ConfigMap',
ksvc: 'Service'
};

function loadYaml (fileLocation) {
Expand Down Expand Up @@ -118,7 +119,19 @@ function loadFiles (resourceList, config) {
return backToJSON;
});

return Promise.all(resourcePromises);
return Promise.all(resourcePromises).then((loadedResources) => {
// Filter out the non-knative items if we are working with Knative
if (config.knative) {
return loadedResources.filter((r) => {
if (r.apiVersion && r.apiVersion.includes('serving.knative.dev')) {
return true;
}
return false;
});
} else {
return loadedResources;
}
});
}

function findFiles (config) {
Expand Down

0 comments on commit 88eed5d

Please sign in to comment.