diff --git a/.github/workflows/stackql-exec-test.yml b/.github/workflows/stackql-exec-test.yml index 23e78c3..9f23d7c 100644 --- a/.github/workflows/stackql-exec-test.yml +++ b/.github/workflows/stackql-exec-test.yml @@ -1,4 +1,4 @@ -name: 'stackql query tests' +name: 'stackql-exec tests' on: push: @@ -114,6 +114,21 @@ jobs: GOOGLE_PROJECT: ${{ vars.GOOGLE_PROJECT }} GOOGLE_ZONE: ${{ vars.GOOGLE_ZONE }} + # + # run a query using the `query_file_path`, `data_file_path` and `vars` inputs (dry-run) + # + - name: google query example with query file and data file using vars (dry-run) + id: stackql-query-file-with-data-file-and-vars-dry-run + uses: ./ + with: + query_file_path: './stackql_scripts/google-instances-by-status-with-external-data-file.iql' + data_file_path: './stackql_scripts/google-instances-by-status-with-external-data-file.jsonnet' + vars: GOOGLE_PROJECT=${{ env.GOOGLE_PROJECT }},GOOGLE_ZONE=${{ env.GOOGLE_ZONE }} + dry_run: true + env: + GOOGLE_PROJECT: ${{ vars.GOOGLE_PROJECT }} + GOOGLE_ZONE: ${{ vars.GOOGLE_ZONE }} + # # run a query using the `query_file_path`, `data_file_path` and `vars` inputs # diff --git a/README.md b/README.md index 0b80b71..3129bba 100644 --- a/README.md +++ b/README.md @@ -7,21 +7,24 @@ Github Action as a wrapper for executing a single command in stackql, maps all s Authentication to StackQL providers is done via environment variables source from GitHub Actions Secrets. To learn more about authentication, see the setup instructions for your provider or providers at the [StackQL Provider Registry Docs](https://stackql.io/registry). ## Inputs -- `query` - stackql query to execute **(need to supply either `query` or `query_file_path`)** -- `query_file_path` - stackql query file to execute **(need to supply either `query` or `query_file_path`)** -- `data_file_path` - (optional) path to data file to pass to the stackql query preprocessor (`json` or `jsonnet`) -- `vars` - (optional) comma delimited list of variables to pass to the stackql query preprocessor (supported with `jsonnet` config blocks or `jsonnet` data files only), accepts `var1=val1,var2=val2`, can be used to source environment variables into stackql queries -- `query_output` - (optional) output format of the stackql exec result, accepts `table`, `csv`, `json` and `text`, defaults to `json` -- `auth_obj_path` - (optional) the path of json file that stores stackql AUTH string **(only required when using non-standard environment variable names)** -- `auth_str` - (optional) stackql AUTH string **(only required when using non-standard environment variable names)** -- `is_command` - (optional defaults to 'false') set to true if the stackql execution is a command that does not return data +- **`query`** - stackql query to execute *(need to supply either __`query`__ or __`query_file_path`__)* +- **`query_file_path`** - stackql query file to execute *(need to supply either __`query`__ or __`query_file_path`__)* +- **`data_file_path`** - (optional) path to data file to pass to the stackql query preprocessor (`json` or `jsonnet`) +- **`dry_run`** - (optional) set to `true` to print the query that would be executed without actually executing it (default is `false`) +- **`vars`** - (optional) comma delimited list of variables to pass to the stackql query preprocessor (supported with `jsonnet` config blocks or `jsonnet` data files only), accepts `var1=val1,var2=val2`, can be used to source environment variables into stackql queries +- **`query_output`** - (optional) output format of the stackql exec result, accepts `table`, `csv`, `json` and `text`, defaults to `json` +- **`auth_obj_path`** - (optional) the path of json file that stores stackql AUTH string *(only required when using non-standard environment variable names)* +- **`auth_str`** - (optional) stackql AUTH string *(only required when using non-standard environment variable names)* +- **`is_command`** - (optional) set to `true` if the stackql execution is a command that does not return data (defaults to `false`) +- **`on_failure`** - (optional) behavior on a failure in query, supported values are `exit` (default) and `continue` ## Outputs This action uses [setup-stackql](https://github.com/marketplace/actions/stackql-studio-setup-stackql), with use_wrapper set to `true`, `stdout` and `stderr` are set to `exec-result` and `exec-error` -- `exec-result` - The STDOUT stream of the call to the `stackql` binary. -- `exec-error` - The STDERR stream of the call to the `stackql` binary. +- **`stackql-query-results`** - results from a stackql query (in the format specified) +- **`stackql-command-output`** - text output from a stackql command (a query that does not return data) +- **`stackql-query-error`** - error from a stackql query ## Examples diff --git a/action.yml b/action.yml index f1e430d..51eac96 100644 --- a/action.yml +++ b/action.yml @@ -11,6 +11,10 @@ inputs: data_file_path: description: jsonnet or json data file to be passed to query preprocessor required: false + dry_run: + description: set to true to print the query that would be executed without actually executing it (default is false) + required: false + default: 'false' vars: description: comma delimited list of vars to be passed to query preprocessor (supported with jsonnet config blocks or jsonnet data files only) required: false @@ -91,6 +95,7 @@ runs: QUERY_FILE_PATH: ${{ inputs.query_file_path }} QUERY: ${{inputs.query}} DATA_FILE_PATH: ${{inputs.data_file_path}} + DRY_RUN: ${{inputs.dry_run}} OUTPUT: ${{inputs.query_output}} VARS: ${{inputs.vars}} @@ -103,9 +108,10 @@ runs: const utilsPath = path.join(process.env.GITHUB_ACTION_PATH, 'lib', 'utils.js') const {execStackQLQuery} = require(utilsPath); const onFailure = process.env.ON_FAILURE || 'exit'; // default to 'exit' if not specified - await execStackQLQuery(core, process.env.STACKQL_COMMAND, process.env.IS_COMMAND === 'true', onFailure); + await execStackQLQuery(core, process.env.STACKQL_COMMAND, process.env.IS_COMMAND === 'true', onFailure, process.env.DRY_RUN === 'true'); env: IS_COMMAND: ${{ inputs.is_command }} + DRY_RUN: ${{ inputs.dry_run }} ON_FAILURE: ${{ inputs.on_failure }} branding: diff --git a/lib/tests/failed-result.json b/lib/tests/failed-result.json deleted file mode 100644 index 82e6d31..0000000 --- a/lib/tests/failed-result.json +++ /dev/null @@ -1 +0,0 @@ -[{"name":"stackql-demo-001","status":"RUNNING"}] \ No newline at end of file diff --git a/lib/tests/success-result.json b/lib/tests/success-result.json deleted file mode 100644 index 030276f..0000000 --- a/lib/tests/success-result.json +++ /dev/null @@ -1 +0,0 @@ -[{"name":"stackql-demo-001","status":"TERMINATED"}] \ No newline at end of file diff --git a/lib/tests/test-auth.json b/lib/tests/test-auth.json deleted file mode 100644 index d8de066..0000000 --- a/lib/tests/test-auth.json +++ /dev/null @@ -1 +0,0 @@ -{ "google": { "type": "service_account", "credentialsfilepath": "sa-key.json" }} \ No newline at end of file diff --git a/lib/tests/utils.test.js b/lib/tests/utils.test.js index 5a74e71..759495c 100644 --- a/lib/tests/utils.test.js +++ b/lib/tests/utils.test.js @@ -4,7 +4,9 @@ describe("Utils Functions Tests", () => { let core; beforeEach(() => { + core = { + debug: jest.fn().mockImplementation((message) => console.log(`DEBUG: ${message}`)), setFailed: jest.fn().mockImplementation((message) => console.error(message)), info: jest.fn().mockImplementation((message) => console.log(message)), exportVariable: jest.fn(), diff --git a/lib/utils.js b/lib/utils.js index 65a5c3b..3b4a422 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -38,9 +38,10 @@ function setupAuth(core) { async function getStackqlCommand(core) { - const [query, queryFilePath, auth, output = "json", vars, dataFilePath] = [ + const [query, queryFilePath, dry_run, auth, output = "json", vars, dataFilePath] = [ process.env.QUERY, process.env.QUERY_FILE_PATH, + process.env.DRY_RUN, process.env.AUTH, process.env.OUTPUT, process.env.VARS, @@ -94,6 +95,12 @@ async function getStackqlCommand(core) { args.push(`--output ${output}`); + if (checkEnvVarValid(dry_run)) { + if(dry_run === "true"){ + args.push(`--dryrun`); + } + } + if (checkEnvVarValid(auth)) { args.push(`--auth "${auth}"`); } @@ -107,7 +114,7 @@ async function getStackqlCommand(core) { try { const stackQLCommand = `${stackQLExecutable} ${args.join(" ")}`; core.exportVariable('STACKQL_COMMAND', `${stackQLCommand}`); - core.info(`STACKQL_COMMAND: ${stackQLCommand}`); + core.debug(`STACKQL_COMMAND: ${stackQLCommand}`); } catch (error) { core.error(error); core.setFailed("error when executing stackql"); @@ -124,16 +131,22 @@ const checkEnvVarValid = (variable) => { * @param {string} command - The StackQL command to be executed. * @param {boolean} isCommand - Indicates if the operation is a command (true) or query (false). * @param {string} onFailure - The action to take if the command fails. Either 'exit' or 'continue'. + * @param {boolean} dryRun - Indicates if the operation is a dry run only. */ -async function execStackQLQuery(core, command, isCommand, onFailure) { - - if (onFailure !== 'exit' && onFailure !== 'continue') { - core.setFailed(`onFailure must be 'exit' or 'continue'. Received: ${onFailure}`); - return; +async function execStackQLQuery(core, command, isCommand, onFailure, dryRun) { + + if (dryRun) { + // dry run + core.info(`dry-run enabled, skipping stackql query execution`); + } else { + // real query + if (onFailure !== 'exit' && onFailure !== 'continue') { + core.setFailed(`onFailure must be 'exit' or 'continue'. Received: ${onFailure}`); + return; + } + core.info(`executing stackql query (isCommand: ${isCommand}): ${command}`); } - core.info(`executing stackql query (isCommand: ${isCommand}): ${command}`); - try { let { stdout, stderr } = await execAsync(command); @@ -156,7 +169,12 @@ async function execStackQLQuery(core, command, isCommand, onFailure) { core.debug(`STDOUT: ${stdout}`); if (!isCommand) { core.setOutput('stackql-query-results', stdout); - core.info(`query output:\n${stdout}`); + if(dryRun){ + const json = JSON.parse(stdout); + core.info(`dry-run query:\n${json[0].query}`); + } else { + core.info(`query output:\n${stdout}`); + } } } else { core.debug('STDOUT: ');