diff --git a/.env b/.env index 129c4d7..720a012 100644 --- a/.env +++ b/.env @@ -1,2 +1,4 @@ ## Set pro options if applicable -RUNDECK_IMAGE=rundeck/rundeck:4.4.0 +RUNDECK_IMAGE=rundeck/rundeck:4.8.0 +RUNDECK_USER=admin +RUNDECK_PASSWORD=admin \ No newline at end of file diff --git a/client/rundeck-cli/src/commands/addUsers.ts b/client/rundeck-cli/src/commands/addUsers.ts index 435c943..816bead 100644 --- a/client/rundeck-cli/src/commands/addUsers.ts +++ b/client/rundeck-cli/src/commands/addUsers.ts @@ -1,7 +1,7 @@ import {Argv} from'yargs' -import {waitForRundeckReady, createStoragePassword, createProject, asyncForEach, createStoragePrivateKey, createAcl} from '../lib/util' +import {createWaitForRundeckReady, runeckLoginToken, asyncForEach, loadConfigYaml} from '../lib/util' -import { Rundeck, PasswordCredentialProvider}from 'ts-rundeck' +import { Rundeck, PasswordCredentialProvider} from 'ts-rundeck' import Path from 'path' import * as FS from '../async/fs' import { JobUuidOption } from 'ts-rundeck/dist/lib/models' @@ -12,6 +12,8 @@ import { collapseTextChangeRangesAcrossMultipleVersions } from 'typescript' interface Opts { rundeck_url: string, + username: string, + password: string, config_file: string, path: string, debug: boolean @@ -36,6 +38,20 @@ builder(yargs: Argv) { default: false, require: true }) + .option("ru", { + alias: "username", + describe: "Rundeck Username", + type: 'string', + default: false, + require: true + }) + .option("rp", { + alias: "password", + describe: "Rundeck password", + type: 'string', + default: false, + require: true + }) .option("f", { alias: "config_file", describe: "Config file", @@ -69,12 +85,14 @@ builder(yargs: Argv) { const users: User[] = config.users; - const username = 'admin' - const password = 'admin' - const client = new Rundeck(new PasswordCredentialProvider(rundeckUrl, username, password), {baseUri: rundeckUrl}) + const username = opts.username + const password = opts.password + const client = new Rundeck(new PasswordCredentialProvider(rundeckUrl, username, password), {noRetryPolicy: true, baseUri: rundeckUrl}) - console.log("Waiting for Rundeck"); - await waitForRundeckReady(client); + console.log("Waiting for Rundeck"); + await createWaitForRundeckReady( + () => client,5 * 60 * 1000) + console.log("Rundeck started!!!"); console.log("----------------------------------"); diff --git a/client/rundeck-cli/src/commands/loadProject.ts b/client/rundeck-cli/src/commands/loadProject.ts index 01aff84..575fdd6 100644 --- a/client/rundeck-cli/src/commands/loadProject.ts +++ b/client/rundeck-cli/src/commands/loadProject.ts @@ -1,7 +1,7 @@ import {Argv} from'yargs' -import {waitForRundeckReady, createStoragePassword, createProject, asyncForEach, createStoragePrivateKey, createAcl} from '../lib/util' +import {createWaitForRundeckReady, createStoragePassword, createProject, asyncForEach, createStoragePrivateKey, loadConfigYaml, createAcl, runeckLoginToken} from '../lib/util' -import { Rundeck, PasswordCredentialProvider}from 'ts-rundeck' +import { Rundeck, PasswordCredentialProvider, TokenCredentialProvider,}from 'ts-rundeck' import Path from 'path' import * as FS from '../async/fs' import { JobUuidOption } from 'ts-rundeck/dist/lib/models' @@ -9,9 +9,12 @@ import YAML from 'yaml' import fetch from 'node-fetch'; import FormData from 'form-data'; import { collapseTextChangeRangesAcrossMultipleVersions } from 'typescript' +import { RequestPolicyFactory } from "@azure/ms-rest-js" interface Opts { rundeck_url: string, + username: string, + password: string, config_file: string, path: string, debug: boolean @@ -47,6 +50,20 @@ builder(yargs: Argv) { default: false, require: true }) + .option("ru", { + alias: "username", + describe: "Rundeck Username", + type: 'string', + default: false, + require: true + }) + .option("rp", { + alias: "password", + describe: "Rundeck password", + type: 'string', + default: false, + require: true + }) .option("f", { alias: "config_file", describe: "Config file", @@ -83,12 +100,19 @@ builder(yargs: Argv) { const keys: Key[] = config.keys; const acls: Acl[] = config.acls; + const username = opts.username + const password = opts.password + + const rundeckAuth = await runeckLoginToken(rundeckUrl, username, password) + const token = rundeckAuth["token"] + const client = rundeckAuth["client"] - const username = 'admin' - const password = 'admin' - const client = new Rundeck(new PasswordCredentialProvider(rundeckUrl, username, password), {baseUri: rundeckUrl}) console.log("Waiting for Rundeck"); - await waitForRundeckReady(client); + await createWaitForRundeckReady( + () => new Rundeck(new PasswordCredentialProvider(rundeckUrl, username, password), {noRetryPolicy: true, baseUri: rundeckUrl}), + 5 * 60 * 1000 + ); + console.log("Rundeck started!!!"); console.log("----------------------------------"); @@ -182,22 +206,6 @@ builder(yargs: Argv) { if(projectImport == true){ const importFileName = Path.join(path, project.archive); - const tokenResponse = await client.sendRequest({ - headers: {'Content-Type': 'application/json'}, - pathTemplate: `/api/36/tokens/{username}`, - pathParameters: {username: username}, - baseUrl: rundeckUrl, - method: 'POST', - body: { - "user": username, - "roles": [ - "admin", - ], - "duration": "30d" - } - }); - - let token = tokenResponse.parsedBody.token console.log("import project"); try{ @@ -208,7 +216,7 @@ builder(yargs: Argv) { 'Content-Type': 'application/zip', } - fetch(`${rundeckUrl}/api/38/project/${project_name}/import?importConfig=true&importACL=true&jobUuidOption=preserve&importWebhooks=true`, + fetch(`${rundeckUrl}/api/38/project/${project_name}/import?importConfig=true&importACL=true&jobUuidOption=remove&importWebhooks=true`, { method: 'PUT', body: file, headers:headers, }) //.then(res => console.log(res)); @@ -225,4 +233,4 @@ builder(yargs: Argv) { } -module.exports = new LoadProjectCommand() +module.exports = new LoadProjectCommand() \ No newline at end of file diff --git a/client/rundeck-cli/src/commands/updateProject.ts b/client/rundeck-cli/src/commands/updateProject.ts index 6a412a6..7308f4f 100644 --- a/client/rundeck-cli/src/commands/updateProject.ts +++ b/client/rundeck-cli/src/commands/updateProject.ts @@ -1,9 +1,11 @@ import {Argv} from'yargs' -import {waitForRundeckReady, updateProperty} from '../lib/util' -import { Rundeck, PasswordCredentialProvider}from 'ts-rundeck' +import {createWaitForRundeckReady, updateProperty, asyncForEach, loadConfigYaml, runeckLoginToken} from '../lib/util' +import {Rundeck, PasswordCredentialProvider} from 'ts-rundeck' interface Opts { rundeck_url: string, + username: string, + password: string, project_name: string, input_value: string, debug: boolean @@ -22,6 +24,20 @@ builder(yargs: Argv) { default: false, require: true }) + .option("ru", { + alias: "username", + describe: "Rundeck Username", + type: 'string', + default: false, + require: true + }) + .option("rp", { + alias: "password", + describe: "Rundeck password", + type: 'string', + default: false, + require: true + }) .option("p", { alias: "project_name", describe: "Project List", @@ -48,13 +64,19 @@ builder(yargs: Argv) { const project_name = opts.project_name const input_value = opts.input_value const rundeckUrl = opts.rundeck_url + + const username = opts.username + const password = opts.password + const client = new Rundeck(new PasswordCredentialProvider(rundeckUrl, username, password), {baseUri: rundeckUrl}) - - const client = new Rundeck(new PasswordCredentialProvider(rundeckUrl, 'admin', 'admin'), {baseUri: rundeckUrl}) - console.log("----------++++++++++++------------") - console.log("Update Project Script Start") - console.log("Waiting for rundeck") - await waitForRundeckReady(client) + console.log("----------++++++++++++------------"); + console.log("Update Project Script Start"); + + console.log("Waiting for rundeck"); + await createWaitForRundeckReady( + () => client,5 * 60 * 1000) + + console.log("Rundeck started!!!") console.log("----------------------------------"); diff --git a/client/rundeck-cli/src/lib/util.ts b/client/rundeck-cli/src/lib/util.ts index 1fe7c03..e97ccf0 100644 --- a/client/rundeck-cli/src/lib/util.ts +++ b/client/rundeck-cli/src/lib/util.ts @@ -1,7 +1,9 @@ -import {Rundeck} from 'ts-rundeck' import * as dotenv from "dotenv"; import YAML from 'yaml' +import {WebResource, RequestPolicy, RequestPolicyFactory, RequestPolicyOptions, BaseRequestPolicy, HttpOperationResponse} from '@azure/ms-rest-js' +import { Rundeck, PasswordCredentialProvider, TokenCredentialProvider}from 'ts-rundeck' +import {combineCookies} from 'ts-rundeck/dist/util' export function sleep(ms: number): Promise<{}> { return new Promise( res => { @@ -9,6 +11,7 @@ export function sleep(ms: number): Promise<{}> { }) } + export async function waitForRundeckReady(client: Rundeck, timeout = 500000) { await createWaitForRundeckReady(() => client, timeout) } @@ -129,10 +132,10 @@ export function loadConfigYaml(importYaml: string): any{ const env = process.env; - console.log(importYaml); + //console.log(importYaml); Object.keys(env).forEach(function(key) { - console.log('export ' + key + '="' + env[key] +'"'); + //console.log('export ' + key + '="' + env[key] +'"'); let value = env[key] as string; if(importYaml.includes(key)){ @@ -141,8 +144,59 @@ export function loadConfigYaml(importYaml: string): any{ } }); - console.log(importYaml); + //console.log(importYaml); const config = YAML.parse(importYaml) return config } + + +export function cookieEnrichPolicy(cookies: string[]): RequestPolicyFactory { + return { + create: (nextPolicy: RequestPolicy, options: RequestPolicyOptions) => { + return new CookieEnrichPolicy(nextPolicy, options, cookies) + } + } +} + +/** Enriches each request with a set of cookies */ +export class CookieEnrichPolicy extends BaseRequestPolicy { + constructor(nextPolicy: RequestPolicy, options: RequestPolicyOptions, readonly cookies: string[]) { + super(nextPolicy, options) + } + + async sendRequest(webResource: WebResource): Promise { + const reqCookies = webResource.headers.get('cookie') + const combinedCookies = combineCookies(reqCookies, this.cookies) + + webResource.headers.set('cookie', combinedCookies.join(';')) + + return await this._nextPolicy.sendRequest(webResource) + } +} + + +export async function runeckLoginToken(rundeckUrl: string, username: string, password: string ){ + const clientPasswordAuth = new Rundeck(new PasswordCredentialProvider(rundeckUrl, username, password), {noRetryPolicy: true, baseUri: rundeckUrl}) + + const tokenResponse = await clientPasswordAuth.sendRequest({ + headers: {'Content-Type': 'application/json'}, + pathTemplate: `/api/36/tokens/{username}`, + pathParameters: {username: username}, + baseUrl: rundeckUrl, + method: 'POST', + body: { + "user": username, + "roles": [ + "admin", + ], + "duration": "30d" + } + }); + + let token = tokenResponse.parsedBody.token + + const client = new Rundeck(new TokenCredentialProvider(token),{baseUri: rundeckUrl}) + return {token: token, client: client} + +} \ No newline at end of file diff --git a/client/scripts/init.sh b/client/scripts/init.sh index ebd53a7..c80086a 100755 --- a/client/scripts/init.sh +++ b/client/scripts/init.sh @@ -2,7 +2,7 @@ echo "Init Script Starting" ## Place Init Script Code here as needed. This is run as part of DockerFile steps for Client node. ssh-keygen -q -t rsa -N '' -f /rundeck-cli/data/keys/id_rsa -cat $CONFIG_FILE +#cat $CONFIG_FILE chmod +x bin/cli @@ -11,8 +11,8 @@ ls -last /rundeck-cli/data echo "-----" # load project -./bin/cli load --rundeck_url $RUNDECK_URL --config_file "$CONFIG_FILE" --path /rundeck-cli -./bin/cli addUsers --rundeck_url $RUNDECK_URL --config_file "$CONFIG_FILE" --path /rundeck-cli +./bin/cli load --rundeck_url $RUNDECK_URL --username $RUNDECK_USER --password $RUNDECK_PASSWORD --config_file "$CONFIG_FILE" --path /rundeck-cli +./bin/cli addUsers --rundeck_url $RUNDECK_URL --username $RUNDECK_USER --password $RUNDECK_PASSWORD --config_file "$CONFIG_FILE" --path /rundeck-cli # update a value in a specified project # See rundeckpro/sensu-demo repo for detailed example use diff --git a/docker-compose.yml b/docker-compose.yml index b65f932..fdb0695 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,6 +40,8 @@ services: RUNDECK_URL: http://rundeck:4440 RUNDECK_API_URL: http://localhost:4440 RUNDECK_TOKEN: u5gJoKU3NpS6IC4lFmPQFfDkUlZnQOXp + RUNDECK_USER: ${RUNDECK_USER:-admin} + RUNDECK_PASSWORD: ${RUNDECK_PASSWORD:-admin} volumes: - shared-volume:/rundeck-cli/data/keys/ - ./data/user.aclpolicy:/rundeck-cli/data/user.aclpolicy