Skip to content

Commit

Permalink
feat: docker-desktop kube option (#601)
Browse files Browse the repository at this point in the history
This adds the ability to deploy to the docker-desktop kubernetes

The --kube flag can now be a string, like --kube=docker-desktop or --kube=minikube. It can also be used like this --kube and will default to minikube
  • Loading branch information
lholmquist committed Oct 18, 2021
1 parent 3997b53 commit ddd45e9
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 32 deletions.
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,20 @@ _please note: Currently, once a route, service, deployment config, build config,

#### Using with Kubernetes

Nodeshift can deploy Node.js applications to a Kubernetes Cluster using the `--kube` flag. At the moment, there is only support for [minikube](https://minikube.sigs.k8s.io/docs/start/).
Nodeshift can deploy Node.js applications to a Kubernetes Cluster using the `--kube` flag.

Nodeshift expects that your code has a Dockerfile in its root directory. Then deploying to Minikube is as easy as running:
There are 2 options that can be passed. `minikube` or `docker-desktop` . Passing just the `--kube` flag will default to minikube

`npx nodeshift --kube`
Nodeshift expects that your code has a Dockerfile in its root directory. Then deploying to kubernetes is as easy as running:

`npx nodeshift --kube=minikube`

Note on Minikube: This connects to Minikubes docker server, create a new container and then deploy and expose that container with a `Deployment` and `Service`

To learn more about [minikube](https://minikube.sigs.k8s.io/docs/start/).

To learn more about [docker-desktop](https://docs.docker.com/desktop/kubernetes/).

This connect to Minikubes docker server, create a new container and then deploy and expose that container with a `Deployment` and `Service`

#### Openshift Rest Client Configuration

Expand Down
9 changes: 7 additions & 2 deletions bin/nodeshift
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ yargs
type: 'string'
})
.option('kube', {
describe: 'Flag to deploy an application to a vanilla kubernetes cluster. At the moment only Minikube is supported.',
type: 'boolean'
describe: 'Flag to deploy an application to a vanilla kubernetes cluster. At the moment only Minikube is supported.'
})
.option('configLocation', {
describe: 'change the default location of the config',
Expand Down Expand Up @@ -256,6 +255,12 @@ function createOptions (argv) {

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

if (argv.kube === 'docker-desktop') {
options.kube = argv.kube;
} else if (argv.kube === 'minikube' || options.kube) {
options.kube = 'minikube';
}

options.projectLocation = argv.projectLocation;
options.dockerImage = argv.dockerImage;
options.imageTag = argv.imageTag;
Expand Down
12 changes: 6 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ function logout (options = {}) {
@param {array} [options.definedProperties] - Array of objects with the format { key: value }. Used for template substitution
@param {boolean} [options.useDeployment] - Flag to deploy the application using a Deployment instead of a DeploymentConfig. Defaults to false
@param {boolean} [options.knative] - EXPERIMENTAL. flag to deploy an application as a Knative Serving Service. Defaults to false
@param {boolean} [options.kube] - Flag to deploy an application to a vanilla kubernetes cluster. At the moment only Minikube is supported. Defaults to false
@param {string/boolean} [options.kube] - Flag to deploy an application to a vanilla kubernetes cluster. Defaults to false. options are 'minikube' or 'docker-desktop'
@returns {Promise<object>} - Returns a JSON Object
*/
function getNodeshiftConfig (options = {}) {
Expand Down Expand Up @@ -113,7 +113,7 @@ function getNodeshiftConfig (options = {}) {
@param {array} [options.definedProperties] - Array of objects with the format { key: value }. Used for template substitution
@param {boolean} [options.useDeployment] - Flag to deploy the application using a Deployment instead of a DeploymentConfig. Defaults to false
@param {boolean} [options.knative] - EXPERIMENTAL. flag to deploy an application as a Knative Serving Service. Defaults to false
@param {boolean} [options.kube] - Flag to deploy an application to a vanilla kubernetes cluster. At the moment only Minikube is supported. Defaults to false
@param {string/boolean} [options.kube] - Flag to deploy an application to a vanilla kubernetes cluster. Defaults to false. options are 'minikube' or 'docker-desktop'
@returns {Promise<object>} - Returns a JSON Object
*/
function deploy (options = {}) {
Expand Down Expand Up @@ -150,7 +150,7 @@ function deploy (options = {}) {
@param {array} [options.definedProperties] - Array of objects with the format { key: value }. Used for template substitution
@param {boolean} [options.useDeployment] - Flag to deploy the application using a Deployment instead of a DeploymentConfig. Defaults to false
@param {boolean} [options.knative] - EXPERIMENTAL. flag to deploy an application as a Knative Serving Service. Defaults to false
@param {boolean} [options.kube] - Flag to deploy an application to a vanilla kubernetes cluster. At the moment only Minikube is supported. Defaults to false
@param {string/boolean} [options.kube] - Flag to deploy an application to a vanilla kubernetes cluster. Defaults to false. options are 'minikube' or 'docker-desktop'
@returns {Promise<object>} - Returns a JSON Object
*/
function resource (options = {}) {
Expand Down Expand Up @@ -190,7 +190,7 @@ function resource (options = {}) {
@param {array} [options.definedProperties] - Array of objects with the format { key: value }. Used for template substitution
@param {boolean} [options.useDeployment] - Flag to deploy the application using a Deployment instead of a DeploymentConfig. Defaults to false
@param {boolean} [options.knative] - EXPERIMENTAL. flag to deploy an application as a Knative Serving Service. Defaults to false
@param {boolean} [options.kube] - Flag to deploy an application to a vanilla kubernetes cluster. At the moment only Minikube is supported. Defaults to false
@param {string/boolean} [options.kube] - Flag to deploy an application to a vanilla kubernetes cluster. Defaults to false. options are 'minikube' or 'docker-desktop'
@returns {Promise<object>} - Returns a JSON Object
*/
function applyResource (options = {}) {
Expand Down Expand Up @@ -229,7 +229,7 @@ function applyResource (options = {}) {
@param {array} [options.definedProperties] - Array of objects with the format { key: value }. Used for template substitution
@param {boolean} [options.useDeployment] - Flag to deploy the application using a Deployment instead of a DeploymentConfig. Defaults to false
@param {boolean} [options.knative] - EXPERIMENTAL. flag to deploy an application as a Knative Serving Service. Defaults to false
@param {boolean} [options.kube] - Flag to deploy an application to a vanilla kubernetes cluster. At the moment only Minikube is supported. Defaults to false
@param {string/boolean} [options.kube] - Flag to deploy an application to a vanilla kubernetes cluster. Defaults to false. options are 'minikube' or 'docker-desktop'
@returns {Promise<object>} - Returns a JSON Object
*/
function undeploy (options = {}) {
Expand Down Expand Up @@ -263,7 +263,7 @@ function undeploy (options = {}) {
@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.kube] - Flag to deploy an application to a vanilla kubernetes cluster. At the moment only Minikube is supported. Defaults to false
@param {string/boolean} [options.kube] - Flag to deploy an application to a vanilla kubernetes cluster. Defaults to false. options are 'minikube' or 'docker-desktop'
@returns {Promise<object>} - Returns a JSON Object
*/
function build (options = {}) {
Expand Down
22 changes: 14 additions & 8 deletions lib/config/docker-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ const { readFileSync } = require('fs');
const Docker = require('dockerode');

function dockerClientSetup (options = {}, kubeEnvVars) {
const url = new URL(kubeEnvVars.DOCKER_HOST);
let docker;

const docker = new Docker({
host: url.hostname,
port: url.port || 2375,
ca: readFileSync(`${kubeEnvVars.DOCKER_CERT_PATH}/ca.pem`),
cert: readFileSync(`${kubeEnvVars.DOCKER_CERT_PATH}/cert.pem`),
key: readFileSync(`${kubeEnvVars.DOCKER_CERT_PATH}/key.pem`)
});
if (options.kube === 'minikube') {
const url = new URL(kubeEnvVars.DOCKER_HOST);

docker = new Docker({
host: url.hostname,
port: url.port || 2375,
ca: readFileSync(`${kubeEnvVars.DOCKER_CERT_PATH}/ca.pem`),
cert: readFileSync(`${kubeEnvVars.DOCKER_CERT_PATH}/cert.pem`),
key: readFileSync(`${kubeEnvVars.DOCKER_CERT_PATH}/key.pem`)
});
} else {
docker = new Docker();
}

return docker;
}
Expand Down
11 changes: 7 additions & 4 deletions lib/config/nodeshift-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ async function setup (options = {}) {
const currentContext = kubeConfig.getCurrentContext();
const contexts = kubeConfig.getContexts();

console.log(currentContext);

const currentCluster = contexts.find(context => context.name === currentContext);
const config = {
namespace: {
Expand All @@ -136,11 +138,12 @@ async function setup (options = {}) {
let dockerClient;

if (options.kube) {
logger.info('Using the kubernetes flag.');
logger.info(`Using the kubernetes flag. Using ${options.kube}`);

// Assume minikube for now
// TODO(lholmquist): other kube flavors
const kubeEnvVars = await kubernetesConfig();
let kubeEnvVars;
if (options.kube === 'minikube') {
kubeEnvVars = await kubernetesConfig();
}
// Pass these kube envs to the docker client setup thingy
dockerClient = dockerConfig(options, kubeEnvVars);
}
Expand Down
20 changes: 15 additions & 5 deletions lib/project-archiver.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,13 @@ async function createContainer (config) {
// Find the output that has the image id sha
if (chunkAsString.includes('sha')) {
// Parse that into JSON
const parsedChunk = JSON.parse(chunkAsString);
// Depending on if we have verbose output on or off, the location of the sha is differnt
// traverse the entries to find where the sha is, then split on : and take the last value

imageId = findShaID(parsedChunk);
let parsedChunk;
try {
parsedChunk = JSON.parse(chunkAsString);
imageId = findShaID(parsedChunk);
} catch (err) {
imageId = findShaIDNonJSON(chunkAsString.split(':'));
}
}
});

Expand All @@ -151,6 +153,14 @@ function findShaID (value) {
}
}

function findShaIDNonJSON (value) {
for (let i = 0; i < value.length; i++) {
if (value[i].includes('sha')) {
return value[i + 1].replace('"}}', '');
}
}
}

module.exports = exports = {
archiveAndTar: archiveAndTar,
createContainer: createContainer,
Expand Down
19 changes: 17 additions & 2 deletions test/config-tests/docker-config-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ test('docker-config', (t) => {
DOCKER_HOST: 'tcp://192.168.39.50:2376',
DOCKER_CERT_PATH: '/home/lucasholmquist/.minikube/certs'
};
dockerClientSetup({}, kubeEnvVars);
dockerClientSetup({ kube: 'minikube' }, kubeEnvVars);

t.pass();
t.end();
Expand All @@ -35,7 +35,22 @@ test('docker-config - no port', (t) => {
DOCKER_HOST: 'tcp://192.168.39.50',
DOCKER_CERT_PATH: '/home/lucasholmquist/.minikube/certs'
};
dockerClientSetup({}, kubeEnvVars);
dockerClientSetup({ kube: 'minikube' }, kubeEnvVars);

t.pass();
t.end();
});

test('docker-config - docker-desktop', (t) => {
const dockerClientSetup = proxyquire('../../lib/config/docker-config', {
fs: {
readFileSync: (path) => {
t.fail();
}
}
});

dockerClientSetup({ kube: 'docker-desktop' });

t.pass();
t.end();
Expand Down
55 changes: 54 additions & 1 deletion test/config-tests/nodeshift-config-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -831,7 +831,60 @@ test('nodeshift-config basic setup kube option', (t) => {
});

const options = {
kube: true
kube: 'minikube'
};

const p = nodeshiftConfig(options).then((config) => {
t.equal(config.namespace.name, 'default', 'Kube flag uses default by default');
t.end();
}).catch(t.fail);

t.equal(p instanceof Promise, true, 'should return a Promise');
});

test('nodeshift-config docker-desktop kube option', (t) => {
const nodeshiftConfig = proxyquire('../../lib/config/nodeshift-config', {
'./kubernetes-config': () => {
t.fail();
},
'./docker-config': () => {
t.pass();
return Promise.resolve();
},
'./login-config': {
doNodeshiftLogin: () => {
t.fail('This should not be called');
},
doNodeshiftLogout: () => {
t.fail('This should not be called');
return Promise.resolve();
},
checkForNodeshiftLogin: () => {
t.pass();
return Promise.resolve();
}
},
'openshift-rest-client': {
OpenshiftClient: () => {
return Promise.resolve({
kubeconfig: {
getCurrentContext: () => {
return 'nodey/ip/other';
},
getCurrentCluster: () => {
return { server: 'http://mock-cluster' };
},
getContexts: () => {
return [{ name: 'nodey/ip/other' }];
}
}
});
}
}
});

const options = {
kube: 'docker-desktop'
};

const p = nodeshiftConfig(options).then((config) => {
Expand Down
51 changes: 51 additions & 0 deletions test/project-archiver-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,3 +363,54 @@ test('test build with kube flag ', (t) => {
t.end();
}).catch(t.fail);
});

test('test build with kube flag - finding sha as a string ', (t) => {
const projectArchiver = proxyquire('../lib/project-archiver', {
'./helpers': {
createDir: () => {
t.fail();
return Promise.resolve();
},
cleanUp: () => {
t.fail();
return Promise.resolve();
},
listFiles: () => {
return Promise.resolve([]);
}
},
tar: {
create: () => {
t.fail();
return Promise.resolve();
}
}
});
const { PassThrough } = require('stream');
const mockReadable = new PassThrough();

const config = {
projectPackage: {},
projectName: 'project Name',
kube: true,
dockerClient: {
buildImage: (options, otherOpitons, cb) => {
t.equal(Array.isArray(options.src), true, 'src should be an array');
t.equal(otherOpitons.t, config.projectName, 'should be equal');
t.equal(otherOpitons.q, false, 'verbose output is not suppressed');
return cb(null, mockReadable);
}
}
};

setTimeout(() => {
mockReadable.emit('data', 'beep');
mockReadable.emit('data', '{"aux":"ID":"sha256:85ba84706d6a68157db30baed19a376f9656aa52bd0209f788de0ccbb762e4ab"}}');
mockReadable.emit('end');
}, 100);

projectArchiver.createContainer(config).then((imageId) => {
t.equal(imageId, '85ba84706d6a68157db30baed19a376f9656aa52bd0209f788de0ccbb762e4ab');
t.end();
}).catch(t.fail);
});

0 comments on commit ddd45e9

Please sign in to comment.