Skip to content

Commit

Permalink
feat: nodeshift login/logout (#545)
Browse files Browse the repository at this point in the history
* This adds 2 new commands:  login and logout

This PR adds the `login` command.  It is meant to be run first with either a username/password or a valid token and the api server of the cluster

something like this: `nodeshift login --token=12345 --server=https.......`  then run `nodeshift deploy`.  While you can specify all those argument flags with the deploy command,  this cleans things up a bit

The new login command will write the authtoken and the api server to a login.json file that lives in `PROJECT_LOCATION/tmp/nodeshift/config/login.json`.  this tmp directory is also used in the build and deploy steps,  and should be gitignored.

CLI Usage - Login:

```
$ nodeshift login --username=developer --password=password --server=https://api.server

or

$ nodeshift login --token=12345 --server=https://api.server
```

CLI Usage - Logout

```
$ nodeshift logout
```

API usage using async/await would look something like this:

```
const nodeshift = require('nodeshift');

const options = {
  username: 'kubeadmin',
  password: '...',
  server: '...',
  insecure: true
};

(async () => {
  await nodeshift.login(options);
  await nodeshift.deploy();
  await nodeshift.logout();
})();
```
  • Loading branch information
lholmquist committed Feb 24, 2021
1 parent 509f0a5 commit f197389
Show file tree
Hide file tree
Showing 10 changed files with 982 additions and 0 deletions.
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ or to use in an npm script

By default, if you run just `nodeshift`, it will run the `deploy` goal, which is a shortcut for running `resource`, `build` and `apply-resource`.

**login** - will login to the cluster

**logout** - will logout of the cluster

**resource** - will parse and create the application resources files on disk

**apply-resource** - does the resource goal and then deploys the resources to your running cluster
Expand All @@ -45,6 +49,47 @@ By default, if you run just `nodeshift`, it will run the `deploy` goal, which is
**undeploy** - removes resources that were deployed with the apply-resource command


### Using Login and Logout

By default, the Nodeshift CLI will look for a kube config in `~/.kube/config`. This is usually created when a user does an `oc login`, but that requires the `oc` to be installed and the extra step of running the `oc login` command. The Nodeshift CLI allows you to pass a username/password or a valid auth token along with the clusters API server address to authenticate requests without the need to run `oc login` first.

While these parameters can be specified for each command, the `nodeshift login` command helps to simplify that. You can now run `nodeshift login` with the parameters mentioned to first login, then run the usual `nodeshift deploy` without neededing to add the flags.

CLI Usage - Login:

```
$ nodeshift login --username=developer --password=password --server=https://api.server
or
$ nodeshift login --token=12345 --server=https://api.server
```

CLI Usage - Logout

```
$ nodeshift logout
```

API usage using async/await would look something like this:

```
const nodeshift = require('nodeshift');
const options = {
username: 'kubeadmin',
password: '...',
server: '...',
insecure: true
};
(async () => {
await nodeshift.login(options);
await nodeshift.deploy();
await nodeshift.logout();
})();
```

### `.nodeshift` Directory

The `.nodeshift` directory contains your resource fragments. These are `.yml` files that describe your services, deployments, routes, etc. By default, nodeshift will create a `Service` and `DeploymentConfig` in memory, if none are provided. A `Route` resource fragment should be provided or use the `expose` flag if you want to expose your application to the outside world.
Expand Down Expand Up @@ -209,6 +254,9 @@ Use server instead. apiServer to pass into the openshift rest client for logging
#### insecure
flag to pass into the openshift rest client for logging in with a self signed cert. Only used with apiServer login. default to false.

#### forceLogin
Force a login when using the apiServer login. Only used with apiServer login. default to false

#### imageTag
Specify the tag of the docker image to use for the deployed application. defaults to latest.
These version tags correspond to the RHSCL tags of the [ubi8/nodejs s2i images](https://access.redhat.com/containers/#/registry.access.redhat.com/ubi8/nodejs-14)
Expand Down Expand Up @@ -279,6 +327,8 @@ Shows the below help
nodeshift resource resource command
nodeshift apply-resource apply resource command
nodeshift undeploy undeploy resources
nodeshift login login to the cluster
nodeshift logout logout of the cluster

Options:
--version Show version number [boolean]
Expand All @@ -300,6 +350,7 @@ Shows the below help
--insecure flag to pass into the openshift rest client for
logging in with a self signed cert. Only used with
apiServer login [boolean]
--forceLogin Force a login when using the apiServer login[boolean]
--imageTag The tag of the docker image to use for the deployed
application. [string] [default: "latest"]
--web-app flag to automatically set the appropriate docker image
Expand Down
3 changes: 3 additions & 0 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ const kubeUrl = require('../lib/kube-url');
module.exports = async function run (options) {
try {
const config = await nodeshiftConfig(options);
if (options.cmd === 'login' || options.cmd === 'logout') {
return config;
}
const response = {};

switch (options.cmd) {
Expand Down
8 changes: 8 additions & 0 deletions bin/nodeshift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ yargs
.command('resource', 'resource command', { cmd: { default: 'resource' } }, commandHandler)
.command('apply-resource', 'apply resource command', { cmd: { default: 'apply-resource' } }, commandHandler)
.command('undeploy [removeAll]', 'undeploy resources', { cmd: { default: 'undeploy' } }, commandHandler)
.command('login', 'login to the cluster', { cmd: { default: 'login' } }, commandHandler)
.command('logout', 'logout of the cluster', { cmd: { default: 'logout' } }, commandHandler)
.option('projectLocation', {
describe: 'change the default location of the project',
type: 'string'
Expand Down Expand Up @@ -74,6 +76,10 @@ yargs
describe: 'flag to pass into the openshift rest client for logging in with a self signed cert. Only used with apiServer login',
type: 'boolean'
})
.options('forceLogin', {
describe: 'Force a login when using the apiServer login',
type: 'boolean'
})
.options('dockerImage', {
describe: 'the s2i image to use, defaults to registry.access.redhat.com/ubi8/nodejs-14',
type: 'string'
Expand Down Expand Up @@ -232,6 +238,8 @@ function createOptions (argv) {

options.token = argv.token;

options.forceLogin = argv.forceLogin;

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

options.knative = argv.knative === true || argv.knative === 'true';
Expand Down
37 changes: 37 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,36 @@ const cli = require('./bin/cli');
All methods take an options object
*/

/**
The login function will login
@param {object} [options] - Options object for the deploy function
@param {string} [options.projectLocation] - the location(directory) of your projects package.json. Defaults to `process.cwd`
@param {string} [options.token] - auth token to pass into the openshift rest client for logging in with the API Server. Overrides the username/password
@param {string} [options.username] - username to pass into the openshift rest client for logging in with the API Server
@param {string} [options.password] - password to pass into the openshift rest client for logging in with the API Server
@param {string} [options.apiServer] - @deprecated - use server instead. apiServer to pass into the openshift rest client for logging in with the API Server
@param {string} [options.server] - server to pass into the openshift rest client for logging in with the API Server
@param {string} [options.insecure] - flag to pass into the openshift rest client for logging in with a self signed cert. Only used with apiServer login. default to false
@param {string} [options.forceLogin] - Force a login when using the apiServer login. Only used with apiServer login. default to false
@returns {Promise<object>} - Returns a JSON Object
*/
function login (options = {}) {
options.cmd = 'login';
return cli(options);
}

/**
The login function will login
@param {object} [options] - Options object for the deploy function
@param {string} [options.projectLocation] - the location(directory) of your projects package.json. Defaults to `process.cwd`
*/
function logout (options = {}) {
options.cmd = 'logout';
return cli(options);
}

/**
The deploy function will do the combination of resource, build and apply-resource
Expand All @@ -18,6 +48,7 @@ const cli = require('./bin/cli');
@param {string} [options.apiServer] - @deprecated - use server instead. apiServer to pass into the openshift rest client for logging in with the API Server
@param {string} [options.server] - server to pass into the openshift rest client for logging in with the API Server
@param {string} [options.insecure] - flag to pass into the openshift rest client for logging in with a self signed cert. Only used with apiServer login. default to false
@param {string} [options.forceLogin] - Force a login when using the apiServer login. Only used with apiServer login. default to false
@param {boolean} [options.expose] - Set to true to create a default Route and expose the default service. defaults to false
@param {object} [options.namespace] -
@param {string} [options.namespace.displayName] - flag to specify the project namespace display name to build/deploy into. Overwrites any namespace settings in your OpenShift or Kubernetes configuration files
Expand Down Expand Up @@ -59,6 +90,7 @@ function deploy (options = {}) {
@param {string} [options.apiServer] - @deprecated - use server instead. apiServer to pass into the openshift rest client for logging in with the API Server
@param {string} [options.server] - server to pass into the openshift rest client for logging in with the API Server
@param {string} [options.insecure] - flag to pass into the openshift rest client for logging in with a self signed cert. Only used with apiServer login. default to false
@param {string} [options.forceLogin] - Force a login when using the apiServer login. Only used with apiServer login. default to false
@param {boolean} [options.expose] - Set to true to create a default Route and expose the default service. defaults to false
@param {object} [options.namespace] -
@param {string} [options.namespace.displayName] - flag to specify the project namespace display name to build/deploy into. Overwrites any namespace settings in your OpenShift or Kubernetes configuration files
Expand Down Expand Up @@ -93,6 +125,7 @@ function resource (options = {}) {
@param {string} [options.apiServer] - @deprecated - use server instead. apiServer to pass into the openshift rest client for logging in with the API Server
@param {string} [options.server] - server to pass into the openshift rest client for logging in with the API Server
@param {string} [options.insecure] - flag to pass into the openshift rest client for logging in with a self signed cert. Only used with apiServer login. default to false
@param {string} [options.forceLogin] - Force a login when using the apiServer login. Only used with apiServer login. default to false
@param {boolean} [options.expose] - Set to true to create a default Route and expose the default service. defaults to false
@param {object} [options.namespace] -
@param {string} [options.namespace.displayName] - flag to specify the project namespace display name to build/deploy into. Overwrites any namespace settings in your OpenShift or Kubernetes configuration files
Expand Down Expand Up @@ -131,6 +164,7 @@ function applyResource (options = {}) {
@param {string} [options.apiServer] - @deprecated - use server instead. apiServer to pass into the openshift rest client for logging in with the API Server
@param {string} [options.server] - server to pass into the openshift rest client for logging in with the API Server
@param {string} [options.insecure] - flag to pass into the openshift rest client for logging in with a self signed cert. Only used with apiServer login. default to false
@param {string} [options.forceLogin] - Force a login when using the apiServer login. Only used with apiServer login. default to false
@param {object} [options.namespace] -
@param {string} [options.namespace.displayName] - flag to specify the project namespace display name to build/deploy into. Overwrites any namespace settings in your OpenShift or Kubernetes configuration files
@param {boolean} [options.namespace.remove] - flag to remove the user created namespace. Only applicable for the undeploy command. Must be used with namespace.name
Expand Down Expand Up @@ -169,6 +203,7 @@ function undeploy (options = {}) {
@param {string} [options.apiServer] - @deprecated - use server instead. apiServer to pass into the openshift rest client for logging in with the API Server
@param {string} [options.server] - server to pass into the openshift rest client for logging in with the API Server
@param {string} [options.insecure] - flag to pass into the openshift rest client for logging in with a self signed cert. Only used with apiServer login. default to false
@param {string} [options.forceLogin] - Force a login when using the apiServer login. Only used with apiServer login. default to false
@param {object} [options.namespace] -
@param {string} [options.namespace.displayName] - flag to specify the project namespace display name to build/deploy into. Overwrites any namespace settings in your OpenShift or Kubernetes configuration files
@param {boolean} [options.namespace.create] - flag to create the namespace if it does not exist. Only applicable for the build and deploy command. Must be used with namespace.name
Expand All @@ -192,6 +227,8 @@ function build (options = {}) {
}

module.exports = {
login,
logout,
deploy,
resource,
applyResource,
Expand Down
111 changes: 111 additions & 0 deletions lib/config/login-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
*
* 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 { promisify } = require('util');
const fs = require('fs');
const writeFileAsync = promisify(fs.writeFile);
const readFileAsync = promisify(fs.readFile);
const helpers = require('../helpers');
const rmrf = require('../utils/rmrf');
const { getTokenFromBasicAuth } = require('openshift-rest-client/lib/basic-auth-request');
const logger = require('../common-log')();

// the flow would be something like this:
// nodeshift login --token=....... --server=.... [--namepsace=] or nodeshift login --username --password --server [--namespace]
// nodeshift deploy

// TODO: login like htis oc login --token=sha256~rQmdYxnYg9yRpaP8_L_YTVyo2CsvEMSCWkekq-MLmO0 --server=https://api.ci-ln-g44q3ck-d5d6b.origin-ci-int-aws.dev.rhcloud.com:6443
// TODO: Login with no params - does this just do the default thing the Openshift rest client does

// TODO: add namespace info when logging in?

// TODO: login from API?
// TODO: specify an config file instead or an .rc file
// TODO: some type of validation that a token/user/pass/server has been passed in
const LOGIN_CONFIG_LOCATION = 'tmp/nodeshift/config';

async function checkForNodeshiftLogin (options) {
let loginConfig;

try {
// Check if there is a login.json file available
loginConfig = JSON.parse(await readFileAsync(`${options.projectLocation}/${LOGIN_CONFIG_LOCATION}/login.json`));
logger.info('login.json file found');
} catch (err) {
if (err.code !== 'ENOENT') throw new Error(err);
// If we made it here, there is no file yet
logger.info('No login.json file found');
}

return loginConfig;
}

async function doNodeshiftLogin (options) {
// Check to see if we want to force the login
let loginConfig;
if (!options.forceLogin) {
loginConfig = await checkForNodeshiftLogin(options);

if (loginConfig) {
// TODO: Check the token
return loginConfig;
}
}

logger.info('logging in with nodeshift cli');
// Check to see if there is a token, if so, use that, if not, use the username/password if there
let authToken;
if (options.token) {
// use this token
authToken = options.token;
} else {
authToken = await getTokenFromBasicAuth({ user: options.username, password: options.password, url: options.server, insecureSkipTlsVerify: options.insecure });
}

loginConfig = { token: authToken, server: options.server, insecureSkipTlsVerify: options.insecure };

// Write the token and server to a file
// Create the directory
await helpers.createDir(`${options.projectLocation}/${LOGIN_CONFIG_LOCATION}`);

// Now write the json to a file
await writeFileAsync(`${options.projectLocation}/${LOGIN_CONFIG_LOCATION}/login.json`, JSON.stringify(loginConfig, null, 2), { encoding: 'utf8' });

return loginConfig;
}

async function doNodeshiftLogout (options) {
try {
// Check if there is a login.json file available
await readFileAsync(`${options.projectLocation}/${LOGIN_CONFIG_LOCATION}/login.json`);
logger.info('Removing login.json to logout');
await rmrf(`${options.projectLocation}/${LOGIN_CONFIG_LOCATION}`);
} catch (err) {
if (err.code !== 'ENOENT') throw new Error(err);
// If we made it here, there is no file yet
logger.info('No login.json file found');
}
}

module.exports = {
doNodeshiftLogin,
doNodeshiftLogout,
checkForNodeshiftLogin
};
17 changes: 17 additions & 0 deletions lib/config/nodeshift-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

const logger = require('../common-log')();
const kubernetesConfig = require('./kubernetes-config');
const { doNodeshiftLogin, doNodeshiftLogout, checkForNodeshiftLogin } = require('./login-config');
const dockerConfig = require('./docker-config');
const { OpenshiftClient: openshiftRestClient } = require('openshift-rest-client');
const fs = require('fs');
Expand All @@ -40,8 +41,24 @@ async function setup (options = {}) {

logger.info('loading configuration');
const projectPackage = JSON.parse(await readFile(`${options.projectLocation}/package.json`, { encoding: 'utf8' }));

if (options.cmd === 'login') {
return doNodeshiftLogin(options);
} else if (options.cmd === 'logout') {
console.log('logout command');
return doNodeshiftLogout(options);
}

let restClientConfig;

// TODO: look for the login.json that might be there
const loginConfig = await checkForNodeshiftLogin(options);

if (loginConfig) {
options.server = loginConfig.server;
options.token = loginConfig.token;
options.insecure = loginConfig.insecureSkipTlsVerify;
}
// If there is a configLocation string, pass it in
if (options.configLocation) {
restClientConfig = options.configLocation;
Expand Down

0 comments on commit f197389

Please sign in to comment.