diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d81fc1..2b92059 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,10 +17,12 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ### Fixed +[BUG] clean up quotes from strings with spaces #47 + --- -## [1.11.0] - (10-24-2024) +## [1.11.1] - (12-11-2024) ### Fixed @@ -29,6 +31,8 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how - Most of the time they are applied at the "service group" - [RFE] Processing vservers using IPv6 addresses #43 - [RFE] ns json output to main work flow #45 +- [BUG] options parsing breaks with spaces/quotes/special-chars #46 +- [BUG] not capturing DISABLED state of serviceGroup/service members #49 --- diff --git a/f5_flipper_test.tgz b/f5_flipper_test.tgz index 7279f89..98fd9b2 100644 Binary files a/f5_flipper_test.tgz and b/f5_flipper_test.tgz differ diff --git a/src/digLbVserver.ts b/src/digLbVserver.ts index 20c0e10..599f01b 100644 --- a/src/digLbVserver.ts +++ b/src/digLbVserver.ts @@ -59,20 +59,6 @@ export async function digLbVserver(coa: AdcConfObj, rx: AdcRegExTree) { lines: [originalString] } - // app.name = app.name.replace(/"/g, '') - - // if (app.name === '"1 APPLE_443_HTTPS"') { - - // debugger; - // const x = RegExp(/\w+|"[\w\s]*"/); - // const y = x.test(app.name) - // const v = app.name.split(/\w+|"[\w\s]*"/gm) - // const z = y; - // } - - // function nameFilter() - - // start with 'bind lb vserver' // todo: update this filter to accomodate names with spaces, see above; @@ -103,66 +89,72 @@ export async function digLbVserver(coa: AdcConfObj, rx: AdcRegExTree) { const serviceName = rxMatch.groups?.service; // dig "add service with supporting bind service lines" + await digService(serviceName, app, coa, rx) // dig service details -> do we have a service with this name? // there should only be one "add service" with this name since we are looking in this specific "bind lb vserver" - const serviceD = coa.add?.service?.filter(s => { - const name = serviceName; - const addServices = coa.add.service; - // pull out the app name from the binding - const sname = s.match(/^(?("[\w.\- ]+"|[\w.\-]+)) (?("[\w.\- ]+"|[\w.\-]+))?/)?.groups?.name; - // does the app name match the bind lb object? - const y = name === sname; - return y; - }); - for await (const x of serviceD) { - const parent = 'add service'; - const originalString = parent + ' ' + serviceD; - app.lines.push(originalString); - const rxMatch = x.match(rx.parents[parent]) - const opts = parseNsOptions(rxMatch.groups?.opts, rx); - if (!rxMatch) { - /* istanbul ignore next */ - return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); - } - - // if we have service details create array to put them - if (!app.bindings.service) { - app.bindings.service = []; - } - - let serviceDetails = { - name: serviceName, - protocol: rxMatch.groups.protocol, - port: rxMatch.groups.port, - server: rxMatch.groups.server, - opts - } - - // todo: is this where we should dig the service binding options for -monitor references? - - // dig "bind service ..." - await digBindService(serviceName, app, coa, rx) - - - // dig "bind ssl service ..." - await digBindSslService(serviceName, app, coa, rx) - - // also get server reference under 'add server ' - if (rxMatch.groups.server) { - - // dig server from serviceGroup server reference - await digServer(rxMatch.groups.server, app, coa, rx) - .then(i => { - if (i) { - serviceDetails = Object.assign(serviceDetails, i) - } - }) - } - - // push service details to app config - app.bindings.service.push(serviceDetails) - } + // const serviceD = coa.add?.service?.filter(s => { + // const name = serviceName; + // const addServices = coa.add.service; + // // pull out the app name from the binding + // const sname = s.match(/^(?("[\w.\- ]+"|[\w.\-]+)) (?("[\w.\- ]+"|[\w.\-]+))?/)?.groups?.name; + // // does the app name match the bind lb object? + // const y = name === sname; + // return y; + // }); + + + // if(serviceD.length > 0) { + + // for await (const x of serviceD) { + // const parent = 'add service'; + // const originalString = parent + ' ' + serviceD; + // app.lines.push(originalString); + // const rxMatch = x.match(rx.parents[parent]) + // const opts = parseNsOptions(rxMatch.groups?.opts, rx); + // if (!rxMatch) { + // /* istanbul ignore next */ + // return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); + // } + + // // if we have service details create array to put them + // if (!app.bindings.service) { + // app.bindings.service = []; + // } + + // let serviceDetails = { + // name: serviceName, + // protocol: rxMatch.groups.protocol, + // port: rxMatch.groups.port, + // server: rxMatch.groups.server, + // opts + // } + + // // todo: is this where we should dig the service binding options for -monitor references? + + // // dig "bind service ..." + // await digBindService(serviceName, app, coa, rx) + + + // // dig "bind ssl service ..." + // await digBindSslService(serviceName, app, coa, rx) + + // // also get server reference under 'add server ' + // if (rxMatch.groups.server) { + + // // dig server from serviceGroup server reference + // await digServer(rxMatch.groups.server, app, coa, rx) + // .then(i => { + // if (i) { + // serviceDetails = Object.assign(serviceDetails, i) + // } + // }) + // } + + // // push service details to app config + // app.bindings.service.push(serviceDetails) + // } + // } // dig serviceGroup details await digServiceGroup(serviceName, app, coa, rx) @@ -172,6 +164,8 @@ export async function digLbVserver(coa: AdcConfObj, rx: AdcRegExTree) { const opts = parseNsOptions(rxMatch.groups?.opts, rx); if (opts['-policyName']) { const pName = opts['-policyName']; + + // initialize the policy array if (!app.bindings['-policyName']) { app.bindings['-policyName'] = []; } @@ -206,7 +200,7 @@ export async function digBindService(serviceName: string, app: AdcApp, obj: AdcC const bindServicesList = obj.bind.service; const sName = s.split(' ')[0] === serviceName - + return sName; }) @@ -297,11 +291,11 @@ export async function digBindService(serviceName: string, app: AdcApp, obj: AdcC */ export async function digPolicy(name: string, app: AdcApp, obj: AdcConfObj, rx: AdcRegExTree) { + // todo: add support for spaces in names + const rwPolicies = obj.add?.rewrite?.policy?.filter(s => s.split(' ')[0] === name) - const policies = obj.add?.rewrite?.policy?.filter(s => s.split(' ')[0] === name) - - if (policies?.length > 0) { - for await (const x of policies) { + if (rwPolicies?.length > 0) { + for await (const x of rwPolicies) { const parent = 'add rewrite policy'; const originalString = parent + ' ' + x; const rxMatch = x.match(rx.parents[parent]); @@ -313,6 +307,24 @@ export async function digPolicy(name: string, app: AdcApp, obj: AdcConfObj, rx: app.lines.push(originalString); } } + + const rsPolicies = obj.add?.responder?.policy?.filter(s => s.split(' ')[0] === name) + + if (rsPolicies?.length > 0) { + for await (const x of rsPolicies) { + const parent = 'add responder policy'; + const originalString = parent + ' ' + x; + const rxMatch = x.match(rx.parents[parent]); + if (!rxMatch) { + /* istanbul ignore next */ + return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); + } + const opts = parseNsOptions(rxMatch.groups?.opts, rx); + + // todo: dig the responder policie actions from namaste app + app.lines.push(originalString); + } + } } @@ -324,7 +336,7 @@ export async function digPolicy(name: string, app: AdcApp, obj: AdcConfObj, rx: * @param obj * @param rx */ -export async function digService(serviceName: string, app: AdcApp, obj: AdcConfObj, rx: AdcRegExTree) { +export async function digService(serviceName: string, app: AdcApp, coa: AdcConfObj, rx: AdcRegExTree) { // this should be a single service name @@ -336,7 +348,7 @@ export async function digService(serviceName: string, app: AdcApp, obj: AdcConfO // filter out the services (single) we need // - const serviceD = obj.add?.service?.filter(s => { + const serviceD = coa.add?.service?.filter(s => { const name = serviceName; // pull out the app name from the binding const sname = s.match(/^(?("[\w.\- ]+"|[\w.\-]+)) (?("[\w.\- ]+"|[\w.\-]+))?/)?.groups?.name; @@ -368,6 +380,27 @@ export async function digService(serviceName: string, app: AdcApp, obj: AdcConfO server: rxMatch.groups.server, opts } + + // dig "bind service ..." + await digBindService(serviceName, app, coa, rx) + + // dig "bind ssl service ..." + await digBindSslService(serviceName, app, coa, rx) + + // also get server reference under 'add server ' + if (rxMatch.groups.server) { + + // dig server from serviceGroup server reference + await digServer(rxMatch.groups.server, app, coa, rx) + .then(i => { + if (i) { + serviceDetails = Object.assign(serviceDetails, i) + } + }) + } + + // push service details to app config + app.bindings.service.push(serviceDetails) } } @@ -386,74 +419,100 @@ export async function digServiceGroup(serviceName: string, app: AdcApp, obj: Adc const serviceGroup: any = {}; // 'add serviceGroup ' - const sgs = obj.add?.serviceGroup?.filter(s => s.split(' ')[0] === serviceName) + const serviceGroupString = obj.add?.serviceGroup?.filter(s => { + const name = serviceName; + // pull out the app name from the binding + const sname = s.match(/^(?("[\w.\- ]+"|[\w.\-]+)) (?("[\w.\- ]+"|[\w.\-]+))?/)?.groups?.name; + // does the app name match the bind lb object? + const y = name === sname; + return y; + })[0] + // should produce one since each "add serviceGroup" will be unique // there is a 1:many with "add serviceGroup" to "bind serviceGroup" - if (sgs?.length > 0) { + if (serviceGroupString) { - for await (const x of sgs) { - const parent = 'add serviceGroup'; - const originalString = parent + ' ' + x; - const rxMatch = x.match(rx.parents[parent]) - if (!rxMatch) { - /* istanbul ignore next */ - return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); - } - const opts = parseNsOptions(rxMatch.groups?.opts, rx); - app.lines.push(originalString); - serviceGroup.name = rxMatch.groups.name; - serviceGroup.protocol = rxMatch.groups.protocol; - deepmergeInto(serviceGroup, opts) + const parent = 'add serviceGroup'; + const originalString = parent + ' ' + serviceGroupString; + const rxMatch = serviceGroupString.match(rx.parents[parent]) + if (!rxMatch) { + /* istanbul ignore next */ + return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); } + const opts = parseNsOptions(rxMatch.groups?.opts, rx); + app.lines.push(originalString); + serviceGroup.name = rxMatch.groups.name; + serviceGroup.protocol = rxMatch.groups.protocol; + deepmergeInto(serviceGroup, opts) } // 'bind serviceGroup ' - obj.bind?.serviceGroup?.filter(s => s.split(' ')[0] === serviceName) - .forEach(async x => { + // can have multiple bingings + const serviceGroupBindings = obj.bind?.serviceGroup?.filter(s => { + const name = serviceName; + // pull out the app name from the binding + const sname = s.match(/^(?("[\w.\- ]+"|[\w.\-]+)) (?("[\w.\- ]+"|[\w.\-]+))?/)?.groups?.name; + // does the app name match the bind lb object? + const y = name === sname; + return y; + }) + if(serviceGroupBindings?.length > 0) { + + for await (const x of serviceGroupBindings) { const parent = 'bind serviceGroup'; const originalString = parent + ' ' + x; + // demo/test at following site + // https://regex101.com/r/uEGKaI/1 const rxMatch = x.match(rx.parents[parent]) - + if (!rxMatch) { /* istanbul ignore next */ return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); } - + const sgbOpts = parseNsOptions(rxMatch?.groups?.opts, rx) + app.lines.push(originalString); - if (rxMatch.groups.serv) { - - - const memberRef = rxMatch.groups.serv.split(' '); - + if (rxMatch?.groups?.serv) { + + // const memberRef = rxMatch.groups.serv.split(' '); + const sgMemberName = rxMatch.groups.serv; + const sgMemberPort = rxMatch.groups.port; + + const sgmOpts = parseNsOptions(rxMatch.groups.mbrOpts, rx) + // dig server from serviceGroup server reference - await digServer(memberRef[0], app, obj, rx) + await digServer(sgMemberName, app, obj, rx) .then(i => { - + const serverDetails = { - name: memberRef[0], - port: memberRef[1] + name: sgMemberName, + port: sgMemberPort } + if (!serviceGroup.servers) serviceGroup.servers = [] - - serviceGroup.servers.push(Object.assign(serverDetails, i)) + // merge all the details together and push to app.json + serviceGroup.servers.push( + Object.assign(serverDetails, i, sgmOpts) + ) }) - - - } else if (rxMatch.groups.monitor) { - - const monitorName = rxMatch.groups.monitor.split(' ').pop(); - + + + } else if (sgbOpts["-monitorName"]) { + + const monitorName = sgbOpts["-monitorName"]; + // add the object param/array if not already there if (!serviceGroup.monitors) serviceGroup.monitors = []; - + //todo: get a list of the default monitor names and add them to the config somehow - + // create the serviceGroup monitor object with the name const monitorObj = { name: monitorName }; - + // get monitor config line + // todo: add support for monitors with spaces in name obj.add?.lb?.monitor?.filter(m => m.split(' ')[0] === monitorName) .forEach(x => { const parent = 'add lb monitor'; @@ -465,28 +524,24 @@ export async function digServiceGroup(serviceName: string, app: AdcApp, obj: Adc return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); } const opts = parseNsOptions(rxMatch.groups.opts, rx); - + // add any monitor object options deepmergeInto(monitorObj, opts) }) - + // push the full monitor object to the serviceGroup serviceGroup.monitors.push(monitorObj); - - } else if (rxMatch.groups.opts) { - deepmergeInto( - serviceGroup, - parseNsOptions(rxMatch.groups.opts, rx) - ) + } - }) + } + } + // if we discovered serviceGroup details, push them to the app.json if (Object.keys(serviceGroup).length > 0) { if (!app.bindings.serviceGroup) app.bindings.serviceGroup = []; app.bindings.serviceGroup.push(serviceGroup) } - // sortNsLines(app.lines, rx) digSslBinding(app, obj, rx) return; diff --git a/src/models.ts b/src/models.ts index 52129e4..5dd1c73 100644 --- a/src/models.ts +++ b/src/models.ts @@ -216,8 +216,8 @@ export type Stats = { export type AdcRegExTree = { adcVersion: RegExp; adcBuild: RegExp; - cfgOptions: RegExp; - cfgOptionsQuotes: RegExp; + // cfgOptions: RegExp; + // cfgOptionsQuotes: RegExp; verbs: RegExp; trimQuotes: RegExp; parents: { @@ -238,8 +238,10 @@ export type AdcRegExTree = { 'add gslb vserver': RegExp; 'add gslb service': RegExp; 'add gslb site': RegExp; - 'add rewrite action': RegExp; 'add rewrite policy': RegExp; + 'add rewrite action': RegExp; + 'add responder policy': RegExp; + 'add responder action': RegExp; 'add appflow policy': RegExp; 'add appflow action': RegExp; 'add appflow collector': RegExp; @@ -287,8 +289,12 @@ export type AdcConfObj = { service?: string[]; } rewrite?: { + policy?: string[]; action?: string[]; + }; + responder?: { policy?: string[]; + action?: string[]; }; cache?: string; dns?: { diff --git a/src/parseAdcUtils.ts b/src/parseAdcUtils.ts index 2f98806..2545469 100644 --- a/src/parseAdcUtils.ts +++ b/src/parseAdcUtils.ts @@ -17,38 +17,65 @@ import { AdcRegExTree } from "./models"; * @param rx regex tree for specific ns adc version * @returns options as an object */ -export function parseNsOptions(str: string, rx: AdcRegExTree): { [k: string]: string } { +export function parseNsOptions(str: string = "", rx: AdcRegExTree): { [k: string]: string } { const obj = {} - // grep out all the options with quotes/spaces - str.match(rx.cfgOptionsQuotes)?.forEach(el => { + // if(str === undefined) return obj; + + // 12.11.2024: this is a hack to get the regex working for now. + // The current rx doesn't pick up the last "-key value" since it uses forward lookups. + str = str.concat(" -devno 12345") + + // grep out all the options with quotes/spaces/normal + // tested with https://regex101.com/r/WCU928/1 + const matches = str.match(/(?-\S+) (?.*?) (?=-\S+)/g); + + matches?.forEach(el => { // split the name off by the first space - const [k, v] = el.split(/ (.*)/) - obj[k] = v; - str = str.replace(el, '') - }) + const k = el.substring(0, el.indexOf(' ')); + // everything after the first space and trim any trailing white space + const v = el.substring(el.indexOf(' ') + 1).trimEnd().replaceAll(/^\"|\"$/g, ""); - // capture everything else without spaces - str.match(rx.cfgOptions)?.forEach(el => { - const [k, v] = el.split(' ') - if (k === '-devno') { - // no nothing, devno is not needed + if(k === '-devno') { + + // skip adding it to the return object } else { - // add to object - obj[k] = v; - str = str.replace(el, '') + + obj[k] = trimQuotes(v); } + str = str.replace(el, '') }) - // // turn certain object values to arrays - // if () { - - // } + // only thing left in the string should be '-devno 123456' + // todo: add some logic to check if other things are left outside -devno and log those details for visibility return obj; } +/** + * detects and trims quotes at the beginning and end of string + * @param s string + * @returns + */ +export function trimQuotes(s: string): string { + + // what is the index of the first " + const first = s.indexOf('"'); + // what is the index of the last " + const last = s.lastIndexOf('"'); + // get the total length of string + const stringL = s.length; + + // Do we have a quote at the beginning and end? + if(first === 0 && last === s.length-1) { + // return the string between the first and last char (") + s = s.substring(1, s.length-1) + } + return s; +} + + /** * sort ns adc config by verbs * add -> set -> bind -> link -> enable -> disable diff --git a/src/regex.ts b/src/regex.ts index 5052c83..9b10937 100644 --- a/src/regex.ts +++ b/src/regex.ts @@ -43,8 +43,8 @@ export class RegExTree { * example; "set ns config -IPAddress 192.168.86.140 -netmask 255.255.255.0" * captures ['-IPAddress 192.168.86.140', '-netmask 255.255.255.0'] */ - public cfgOptions = /-\w+ \S+/g; - public cfgOptionsQuotes = /-\w+ "[\S ]+"/g; + // public cfgOptions = /-\w+ \S+/g; + // public cfgOptionsQuotes = /-\w+ "[\S ]+"/g; private ipAddr = /(?:[0-9]{1,3}\.){3}[0-9]{1,3}/; @@ -54,8 +54,8 @@ export class RegExTree { private regexTree: AdcRegExTree = { adcVersion: this.adcVersionBaseReg, adcBuild: this.adcVersionBuildReg, - cfgOptions: this.cfgOptions, - cfgOptionsQuotes: this.cfgOptionsQuotes, + // cfgOptions: this.cfgOptions, + // cfgOptionsQuotes: this.cfgOptionsQuotes, verbs: /^(add|set|bind|link|enable|disable) /, trimQuotes: /^"(.*)"$/, parents: { @@ -76,8 +76,10 @@ export class RegExTree { 'add gslb vserver': /(?("[\S ]+"|[\S]+)) (?\S+) (?[\S ]+)/, 'add gslb service': /(?("[\S ]+"|[\S]+)) (?\S+) (?\S+) (?(\d+|\*)) (?[\S ]+)/, 'add gslb site': /(?("[\S ]+"|[\S]+)) (?\S+) (?[\S ]+)/, - 'add rewrite action': /(?\S+) (?[\S ]+)/, 'add rewrite policy': /(?\S+) (?[\S ]+)/, + 'add rewrite action': /(?\S+) (?[\S ]+)/, + 'add responder policy': /(?\S+) (?[\S ]+)/, + 'add responder action': /(?\S+) (?[\S ]+)/, 'add appflow policy': /(?\S+) (?[\S]+) (?[\S]+)/, 'add appflow action': /(?\S+) (?[\S ]+)/, 'add appflow collector': /(?\S+) (?[\S ]+)/, @@ -88,7 +90,7 @@ export class RegExTree { 'set ns hostName': /(?[\S ]+)/, 'set gslb vserver': /(?\S+) (?[\S ]+)/, 'bind service': /(?("[\S ]+"|[\S]+)) ((?\S+ (\d+|\*))|(?-monitorName \S+)|(?[\S ]+))/, - 'bind serviceGroup': /(?("[\S ]+"|[\S]+)) ((?\S+ (\d+|\*))|(?-monitorName \S+)|(?[\S ]+))/, + 'bind serviceGroup': /(?("[\S ]+"|[\S]+)) ((?\S+) (?\d+|\*))?(?[\S ]+)?/, 'bind lb vserver': /(?("[\S ]+"|[\S]+)) ((?-[\S ]+)|(?("[\S ]+"|[\S]+)))/, 'bind cs vserver': /(?("[\S ]+"|[\S]+)) (?[\S ]+)/, 'bind ssl service': /(?("[\S ]+"|[\S]+)) (?[\S ]+)/, diff --git a/tests/007_parseNsOpts.unit.tests.ts b/tests/007_parseNsOpts.unit.tests.ts new file mode 100644 index 0000000..6b07448 --- /dev/null +++ b/tests/007_parseNsOpts.unit.tests.ts @@ -0,0 +1,108 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* + * Copyright 2020. F5 Networks, Inc. See End User License Agreement ("EULA") for + * license terms. Notwithstanding anything to the contrary in the EULA, Licensee + * may copy and modify this software product for its internal business purposes. + * Further, Licensee may upload, publish and distribute the modified version of + * the software product on devcentral.f5.com. + */ + +'use strict'; + +import assert from 'assert'; +import { RegExTree } from '../src/regex'; +import { parseNsOptions } from '../src/parseAdcUtils'; + +const events = []; +const parsedFileEvents: any[] = [] +const parsedObjEvents: any[] = [] +const rx = new RegExTree().get('13.1'); + +describe('parse NS options function tests', function () { + + + + + before(async function () { + // log test file name - makes it easer for troubleshooting + console.log(' file:', __filename) + + // clear the events arrays + parsedFileEvents.length = 0 + parsedObjEvents.length = 0 + + }); + + afterEach(function () { + events.length = 0; + }) + + + + // it(`regular options single`, async () => { + + // const items = [ + // 'add server sprout135A_groot 192.168.160.120 -devno 108847', + // 'add server sprout135c_groot 192.168.160.69 -devno 108848', + // 'add server dorsal-nedc 10.8.101.46 -comment "automated deployment"', + // 'add server dorsal-swdc 10.12.101.46 -comment "automated deployment"', + // 'add server stpvec1 stpvec1.f5flipper.com -comment "automated deployment"', + // 'add server stpvec2 stpvec2.f5flipper.com -comment "automated deployment"', + // ]; + + // // strip off all the leading parent object details 'add server ' + // const slim = items.map(x => x.replace('add server ', '')) + + // const rxMatches = slim.map(x => x.match(rx.parents['add server'])) + + // const misses = rxMatches.filter(x => x === undefined) + + // assert.ok(misses.length === 0, 'should not have any rx misses'); + // }) + + it(`lb monitor options complicated`, async () => { + + // this test should accomodate all options on all NS configs. + // just happened to be monitors when this needed to get worked out. + // so add any other config lines with options and add additional tests as needed + + const items = [ + 'add lb monitor test01_http_ecv_mon HTTP-ECV -customHeaders "Host:flippy.doda.com\\r\\n" -send "GET /HealthCheck" -recv "\\\"Version\\\"" -LRTM DISABLED -secure YES -devno 12357', + 'add lb monitor http-custom-8202_mon HTTP -respCode 306-307 -httpRequest "HEAD /" -LRTM DISABLED -interval 10 -resptimeout 5 -destPort 8202 -secure YES -devno 12355', + 'add lb monitor test02_http_80_mon HTTP -respCode 200 -httpRequest "HEAD /to/know/all" -LRTM DISABLED -devno 12345', + 'add lb monitor basic_tcp_monitor TCP -LRTM DISABLED -interval 10 -resptimeout 5 -secure YES -devno 12356', + 'add lb monitor "test with spaces http mon" HTTP-ECV -send "GET /some/complicated/name" -recv OK -LRTM DISABLED -destPort 8081 -devno 12365', + 'add lb monitor redirect_http-mon HTTP -respCode 301 -httpRequest "HEAD /artifactory" -LRTM DISABLED -secure YES -devno 12366', + 'add lb monitor http200_mon HTTP -respCode 200 -httpRequest "GET /api/v1/system/health" -LRTM DISABLED -destPort 8082 -devno 12367', + 'add lb monitor kaizen_http1.1_mon HTTP-ECV -customHeaders "Host:modern.samurai.chi\\r\\n" -send "GET /focusReady" -recv "\"zen\"" -LRTM DISABLED -secure YES -devno 12363', + ]; + + // cut down array of monitors + // these shorter versions better represent what the function will actually see during processing + const slim: string[] = [] + + // strip off all the leading parent object details 'add lb monitor ... ... ' + for await(const x of items) { + //find the index of the first opt "-\S+" + const firstOptIdx = x.match(/ -\S+ /)?.index || 0; + // return the rest of the string from the first match index + const restOfString = x.substring(firstOptIdx); + slim.push(restOfString); + } + + // loop through the array and parse all the ns options + const optsObx = slim.map(i => parseNsOptions(i, rx)); + + assert.deepStrictEqual("Host:flippy.doda.com\\r\\n", optsObx[0]['-customHeaders']); + assert.deepStrictEqual('306-307', optsObx[1]['-respCode']); + assert.deepStrictEqual('HEAD /to/know/all', optsObx[2]['-httpRequest']); + assert.deepStrictEqual('YES', optsObx[3]['-secure']); + assert.deepStrictEqual('8081', optsObx[4]['-destPort']); + assert.deepStrictEqual('DISABLED', optsObx[5]['-LRTM']); + assert.deepStrictEqual("GET /api/v1/system/health", optsObx[6]['-httpRequest']); + assert.deepStrictEqual("Host:modern.samurai.chi\\r\\n", optsObx[7]['-customHeaders']); + assert.deepStrictEqual("GET /focusReady", optsObx[7]['-send']); + }) + + +}); \ No newline at end of file diff --git a/tests/024_service.unit.tests.ts b/tests/024_service.unit.tests.ts index d78149b..add2372 100644 --- a/tests/024_service.unit.tests.ts +++ b/tests/024_service.unit.tests.ts @@ -69,7 +69,7 @@ describe('service abstraction tests', function () { assert.deepStrictEqual(app!.bindings!.service!.length, 3, "should have three service bindings") assert.deepStrictEqual(app!.lines!.length, 16, "should have 16 total lines of ns config") - + }) it(`basic service reference non ssl with monitor`, async () => { @@ -81,7 +81,7 @@ describe('service abstraction tests', function () { assert.deepStrictEqual(app!.bindings!.service!.length, 1, "should have three service bindings") assert.deepStrictEqual(app!.lines!.length, 6, "should have 16 total lines of ns config") - + }) it(`basic service reference ssl`, async () => { @@ -93,7 +93,7 @@ describe('service abstraction tests', function () { assert.deepStrictEqual(app!.bindings!.service!.length, 1, "should have one service bindings") assert.deepStrictEqual(app!.lines!.length, 9, "should have 16 total lines of ns config") - + }) @@ -115,27 +115,27 @@ describe('service abstraction tests', function () { port: "82", server: "SERVERCORE1", opts: { - "-gslb": "NONE", - "-maxClient": "0", - "-maxReq": "0", - "-cip": "ENABLED", - "-ip": "-usip", - "-useproxyport": "YES", - "-sp": "OFF", - "-cltTimeout": "180", - "-svrTimeout": "360", - "-CKA": "NO", - "-TCPB": "NO", - "-CMP": "YES", + "-gslb": "NONE", + "-maxClient": "0", + "-maxReq": "0", + "-cip": "ENABLED client-ip", + "-usip": "NO", + "-useproxyport": "YES", + "-sp": "OFF", + "-cltTimeout": "180", + "-svrTimeout": "360", + "-CKA": "NO", + "-TCPB": "NO", + "-CMP": "YES", }, hostname: "sevcore1.jonny.dev", - }, "should have three service bindings") + }, "should have three service bindings") const addressService = appServices!.filter(x => x.address === "10.240.21.115")[0] // this just confirms that we got back the service with the right "address" and "name" assert.deepStrictEqual(addressService.name, "FUJI02_HTTPS_SVC") - + }) diff --git a/tests/031_sslCerts.unit.tests.ts b/tests/031_sslCerts.unit.tests.ts index 1e94218..31c7b7b 100644 --- a/tests/031_sslCerts.unit.tests.ts +++ b/tests/031_sslCerts.unit.tests.ts @@ -75,7 +75,7 @@ describe('ssl certificate tests', function () { "-key": "www.star.groot_2022.pfx", "-inform": "PFX", "-passcrypt": "XXXX", - "-encrypted": "-encryptmethod", + "-encrypted": "-encryptmethod ENCMTHD_3", profileName: "star.groot.cer", }) diff --git a/tests/artifacts/apps/namaste.conf b/tests/artifacts/apps/namaste.conf new file mode 100644 index 0000000..862a8d8 --- /dev/null +++ b/tests/artifacts/apps/namaste.conf @@ -0,0 +1,40 @@ +### "namaste 443 vip" ########## - Hover for more details - ########## +### 27 lines +### object names with spaces, serviceGroup members DISABLED, SSL-Bridge, full health monitor, service policy +### todo: add parsing of the "-state DISABLED" on the serviceGroup members + +add lb vserver "namaste 443 vip" SSL 10.240.18.68 443 -persistenceType NONE -lbMethod ROUNDROBIN -backupLBMethod LEASTCONNECTION -cltTimeout 180 -devno 50003968 +bind lb vserver "namaste 443 vip" "namaste 8443 svg" +add serviceGroup "namaste 8443 svg" SSL -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO -devno 47218688 +bind serviceGroup "namaste 8443 svg" lotus1.yoga.in 8443 -devno 62357504 +add server lotus1.yoga.in 10.240.20.64 -devno 11415 +bind serviceGroup "namaste 8443 svg" lotus2.yoga.in 8443 -devno 62390272 +add server lotus2.yoga.in 10.240.20.71 -devno 11416 +bind serviceGroup "namaste 8443 svg" lotus3.yoga.in 8443 -devno 62423040 +add server lotus3.yoga.in 10.240.20.72 -devno 11417 + +bind serviceGroup "namaste 8443 svg" dragonfly1.yoga.in 8443 -state DISABLED -devno 62521344 +add server dragonfly1.yoga.in 10.240.24.215 -devno 11476 +bind serviceGroup "namaste 8443 svg" dragonfly2.yoga.in 8443 -state DISABLED -devno 62554112 +add server dragonfly2.yoga.in 10.240.24.225 -devno 11477 +bind serviceGroup "namaste 8443 svg" dragonfly3.yoga.in 8443 -state DISABLED -devno 62586880 +add server dragonfly3.yoga.in 10.240.24.226 -devno 11478 + + +bind serviceGroup "namaste 8443 svg" -monitorName namaste_custome_tcp_mon -devno 62685184 +add lb monitor namaste_custome_tcp_mon TCP -LRTM DISABLED -interval 30 -resptimeout 15 -secure YES -devno 12356 +bind serviceGroup "namaste 8443 svg" -monitorName namaste_awaken_http8443_mon -devno 72876032 +add lb monitor namaste_awaken_http8443_mon HTTP-ECV -send "GET /look/within" -recv "\"find\":love" -LRTM DISABLED -secure YES -devno 12369 + +bind ssl vserver "namaste 443 vip" -cipherName DEFAULT +bind ssl vserver "namaste 443 vip" -certkeyName sinsvault-new +bind ssl vserver "namaste 443 vip" -eccCurveName P_256 +bind ssl vserver "namaste 443 vip" -eccCurveName P_384 +bind ssl vserver "namaste 443 vip" -eccCurveName P_224 +bind ssl vserver "namaste 443 vip" -eccCurveName P_521 + +bind lb vserver "namaste 443 vip" -policyName namaste_443_rsp -priority 100 -gotoPriorityExpression END -type REQUEST + + +add responder policy namaste_443_rsp "HTTP.REQ.URL.EQ(\"/\")" namaste_443_rspa +add responder action namaste_443_rspa redirect "\"https://\" + HTTP.REQ.HOSTNAME.HTTP_URL_SAFE + \"/rebirth/enlightenment=true\"" -responseStatusCode 302 diff --git a/tests/artifacts/f5_flipper_test.tgz b/tests/artifacts/f5_flipper_test.tgz index 7279f89..98fd9b2 100644 Binary files a/tests/artifacts/f5_flipper_test.tgz and b/tests/artifacts/f5_flipper_test.tgz differ diff --git a/tsconfig.json b/tsconfig.json index add7760..ccafcf4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2020", + "target": "ES2022", "module": "commonjs", "moduleResolution": "node", "removeComments": false,