Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add default environment variable and secret #3994

Draft
wants to merge 38 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
d12f69f
Add service account as a default envvar
glasnt Feb 26, 2025
d7d5d6c
add access token as secert value
glasnt Feb 26, 2025
57ddd7f
test availability of new envvars
glasnt Feb 27, 2025
0db05ad
process.env lol
glasnt Feb 27, 2025
279448d
envvars gonna envvar
glasnt Feb 27, 2025
14b0dc5
update ci-dev.yaml
glasnt Feb 27, 2025
cc00d31
add logging for secret
glasnt Feb 27, 2025
b6575f4
revert audit-storage changes
glasnt Feb 27, 2025
816124f
add example usage of ACCESS_TOKEN, SERVICE_ACCOUNT
glasnt Feb 27, 2025
8d75983
revert testing sample
glasnt Feb 27, 2025
775a9f2
make use of access token in a test
glasnt Feb 27, 2025
a8a54a4
doc: update usage
glasnt Feb 27, 2025
33d3a1a
use service account
glasnt Feb 27, 2025
ab6973d
reference correct variable
glasnt Feb 27, 2025
d68a20e
update order, add service_name
glasnt Feb 27, 2025
5392f77
pull envvar
glasnt Feb 27, 2025
2b98c7b
debug: try using id_token
glasnt Feb 27, 2025
68f927c
fix missing renames
glasnt Feb 27, 2025
33e49c2
lint
glasnt Feb 27, 2025
dbd0eda
set the value of the token secret, not the key/string
glasnt Feb 27, 2025
770c57a
lint
glasnt Feb 27, 2025
ed7fb45
update setupvars tests
glasnt Feb 27, 2025
3e0fc19
Merge branch 'main' into default-secrets
glasnt Feb 27, 2025
7053eb3
debug: ¯\_(ツ)_/¯
glasnt Feb 27, 2025
875ffd7
error handling
glasnt Feb 27, 2025
f009b17
what if we just
glasnt Feb 27, 2025
611e5e9
debugging
glasnt Feb 27, 2025
301126d
debugging
glasnt Feb 27, 2025
eefafbd
add custom audience, set by the auth action, updated by e2e test
glasnt Feb 27, 2025
93adf27
debug
glasnt Feb 27, 2025
7f4fe66
lol
glasnt Feb 27, 2025
fed7faa
debugging
glasnt Feb 27, 2025
664b5e7
try separating setup, using a known unused domain
glasnt Feb 27, 2025
d98ab9e
spelling
glasnt Feb 27, 2025
0cb016f
try matching service account
glasnt Feb 27, 2025
b64e2c2
trim
glasnt Feb 27, 2025
b0ce19b
...oh
glasnt Feb 27, 2025
1758aa4
maybe
glasnt Feb 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .github/scripts/setup-vars.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
limitations under the License.
*/

export default function setupVars({projectId, core, setup}, runId = null) {
export default function setupVars({projectId, core, setup, serviceAccount, idToken}, runId = null) {
// Define automatic variables plus custom variables.
const vars = {
PROJECT_ID: projectId,
RUN_ID: runId || uniqueId(),
SERVICE_ACCOUNT: serviceAccount,
...(setup.env || {}),
};

Expand All @@ -45,6 +46,16 @@ export default function setupVars({projectId, core, setup}, runId = null) {
console.log(` ${key}: ${setup.secrets[key]}`);
}

// Set global secret for the Service Account identity token
// Use in place of 'gcloud auth print-identity-token' or auth.getIdTokenClient
// usage: curl -H 'Bearer: $ID_TOKEN' https://

idToken = (idToken || '').toString() // debug
core.exportVariable('ID_TOKEN', idToken)
core.setSecret(idToken)
// For logging, show the source of the ID_TOKEN
console.log(` ID_TOKEN: steps.auth.outputs.id_token (from GitHub Action)`);

// Return env and secrets to use for further steps.
return {
env: env,
Expand Down
32 changes: 17 additions & 15 deletions .github/scripts/setup-vars.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,52 +19,54 @@ import setupVars from './setup-vars.js';
import {substituteVars, uniqueId} from './setup-vars.js';

const projectId = 'my-test-project';
const serviceAccount = "[email protected]"
const core = {
exportVariable: (_key, _value) => null,
setSecret: (_key) => null,
};

const autovars = {PROJECT_ID: projectId, RUN_ID: 'run-id'};
const autovars = {PROJECT_ID: projectId, RUN_ID: 'run-id', SERVICE_ACCOUNT: serviceAccount};

describe('setupVars', () => {
describe('env', () => {
it('empty', () => {
const setup = {};
const vars = setupVars({projectId, core, setup}, 'run-id');
const vars = setupVars({projectId, core, setup, serviceAccount}, 'run-id');
const expected = autovars;
deepStrictEqual(vars.env, expected);
});

it('zero vars', () => {
const setup = {env: {}};
const vars = setupVars({projectId, core, setup}, 'run-id');
const vars = setupVars({projectId, core, setup, serviceAccount}, 'run-id');
const expected = autovars;
deepStrictEqual(vars.env, expected);
});

it('one var', () => {
const setup = {env: {A: 'x'}};
const vars = setupVars({projectId, core, setup}, 'run-id');
const vars = setupVars({projectId, core, setup, serviceAccount}, 'run-id');
const expected = {...autovars, A: 'x'};
deepStrictEqual(vars.env, expected);
});

it('three vars', () => {
const setup = {env: {A: 'x', B: 'y', C: 'z'}};
const vars = setupVars({projectId, core, setup}, 'run-id');
const vars = setupVars({projectId, core, setup, serviceAccount}, 'run-id');
const expected = {...autovars, A: 'x', B: 'y', C: 'z'};
deepStrictEqual(vars.env, expected);
});

it('should override automatic variables', () => {
const setup = {env: {PROJECT_ID: 'custom-value'}};
const vars = setupVars({projectId, core, setup}, 'run-id');
const expected = {PROJECT_ID: 'custom-value', RUN_ID: 'run-id'};
const setup = {env: {PROJECT_ID: 'custom-value', SERVICE_ACCOUNT: '[email protected]'}};
const vars = setupVars({projectId, core, setup, serviceAccount}, 'run-id');
const expected = {PROJECT_ID: 'custom-value', RUN_ID: 'run-id', SERVICE_ACCOUNT: '[email protected]'};
deepStrictEqual(vars.env, expected);
});

it('should interpolate variables', () => {
const setup = {env: {A: 'x', B: 'y', C: '$A/${B}'}};
const vars = setupVars({projectId, core, setup}, 'run-id');
const vars = setupVars({projectId, core, setup, serviceAccount}, 'run-id');
const expected = {...autovars, A: 'x', B: 'y', C: 'x/y'};
deepStrictEqual(vars.env, expected);
});
Expand All @@ -74,7 +76,7 @@ describe('setupVars', () => {
env: {C: '$x/$y'},
secrets: {A: 'x', B: 'y'},
};
const vars = setupVars({projectId, core, setup}, 'run-id');
const vars = setupVars({projectId, core, setup, serviceAccount}, 'run-id');
const expected = {...autovars, C: '$x/$y'};
deepStrictEqual(vars.env, expected);
});
Expand All @@ -83,20 +85,20 @@ describe('setupVars', () => {
describe('secrets', () => {
it('zero secrets', () => {
const setup = {secrets: {}};
const vars = setupVars({projectId, core, setup}, 'run-id');
const vars = setupVars({projectId, core, setup, serviceAccount}, 'run-id');
deepStrictEqual(vars.secrets, '');
});

it('one secret', () => {
const setup = {secrets: {A: 'x'}};
const vars = setupVars({projectId, core, setup}, 'run-id');
const vars = setupVars({projectId, core, setup, serviceAccount}, 'run-id');
const expected = 'A:x';
deepStrictEqual(vars.secrets, expected);
});

it('three secrets', () => {
const setup = {secrets: {A: 'x', B: 'y', C: 'z'}};
const vars = setupVars({projectId, core, setup}, 'run-id');
const vars = setupVars({projectId, core, setup, serviceAccount}, 'run-id');
const expected = 'A:x\nB:y\nC:z';
deepStrictEqual(vars.secrets, expected);
});
Expand All @@ -106,14 +108,14 @@ describe('setupVars', () => {
env: {A: 'x', B: 'y'},
secrets: {C: '$A/$B'},
};
const vars = setupVars({projectId, core, setup}, 'run-id');
const vars = setupVars({projectId, core, setup, serviceAccount}, 'run-id');
const expected = 'C:$A/$B';
deepStrictEqual(vars.secrets, expected);
});

it('should not interpolate secrets', () => {
const setup = {secrets: {A: 'x', B: 'y', C: '$A/$B'}};
const vars = setupVars({projectId, core, setup}, 'run-id');
const vars = setupVars({projectId, core, setup, serviceAccount}, 'run-id');
const expected = 'A:x\nB:y\nC:$A/$B';
deepStrictEqual(vars.secrets, expected);
});
Expand Down
13 changes: 10 additions & 3 deletions .github/workflows/ci-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ jobs:
path: ${{ fromJson(github.event_name == 'pull_request' && needs.affected.outputs.nodejs-paths || '[]') }}
env:
GOOGLE_SAMPLES_PROJECT: long-door-651
GOOGLE_SERVICE_ACCOUNT: [email protected]
CI_SETUP: ${{ toJson(fromJson(needs.affected.outputs.nodejs-setups)[matrix.path])}}
steps:
- name: CI Setup
Expand All @@ -84,20 +85,26 @@ jobs:
with:
node-version: ${{ fromJson(env.CI_SETUP).node-version }}
- uses: google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f # v2
id: auth
with:
project_id: ${{ env.GOOGLE_SAMPLES_PROJECT }}
workload_identity_provider: projects/1046198160504/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider
service_account: [email protected]
service_account: ${{ env.GOOGLE_SERVICE_ACCOUNT }}
access_token_lifetime: 600s # 10 minutes
token_format: 'id_token'
id_token_audience: 'https://action.test/' # service must have this custom audience
id_token_include_email: true
- name: Export environment variables
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
id: vars
with:
script: |
const { default: setupVars } = await import('${{ github.workspace }}/.github/scripts/setup-vars.js')
const { default: setupVars } = await import('${{ github.workspace }}/.github/scripts/setup-vars.js');
const projectId = '${{ env.GOOGLE_SAMPLES_PROJECT }}';
const setup = JSON.parse(process.env.CI_SETUP);
return await setupVars({projectId, core, setup})
const serviceAccount = '${{ env.GOOGLE_SERVICE_ACCOUNT }}';
const idToken = '${{ steps.auth.outputs.id_token }}';
return await setupVars({projectId, core, setup, serviceAccount, idToken})
- uses: google-github-actions/get-secretmanager-secrets@e5bb06c2ca53b244f978d33348d18317a7f263ce # v2
if: ${{ fromJson(steps.vars.outputs.result).secrets }}
with:
Expand Down
13 changes: 10 additions & 3 deletions .github/workflows/ci-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ jobs:
path: ${{ fromJson(github.event_name == 'pull_request' && needs.affected.outputs.nodejs-paths || '[]') }}
env:
GOOGLE_SAMPLES_PROJECT: long-door-651
GOOGLE_SERVICE_ACCOUNT: [email protected]
CI_SETUP: ${{ toJson(fromJson(needs.affected.outputs.nodejs-setups)[matrix.path])}}
steps:
- name: CI Setup
Expand All @@ -96,20 +97,26 @@ jobs:
with:
node-version: ${{ fromJson(env.CI_SETUP).node-version }}
- uses: google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f # v2
id: auth
with:
project_id: ${{ env.GOOGLE_SAMPLES_PROJECT }}
workload_identity_provider: projects/1046198160504/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider
service_account: [email protected]
service_account: ${{ env.GOOGLE_SERVICE_ACCOUNT }}
access_token_lifetime: 600s # 10 minutes
token_format: 'id_token'
id_token_audience: 'https://action.test/' # service must have this custom audience
id_token_include_email: true
- name: Export environment variables
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
id: vars
with:
script: |
const { default: setupVars } = await import('${{ github.workspace }}/.github/scripts/setup-vars.js')
const { default: setupVars } = await import('${{ github.workspace }}/.github/scripts/setup-vars.js');
const projectId = '${{ env.GOOGLE_SAMPLES_PROJECT }}';
const setup = JSON.parse(process.env.CI_SETUP);
return await setupVars({projectId, core, setup})
const serviceAccount = '${{ env.GOOGLE_SERVICE_ACCOUNT }}';
const idToken = '${{ steps.auth.outputs.id_token }}';
return await setupVars({projectId, core, setup, serviceAccount, idToken})
- uses: google-github-actions/get-secretmanager-secrets@e5bb06c2ca53b244f978d33348d18317a7f263ce # v2
if: ${{ fromJson(steps.vars.outputs.result).secrets }}
with:
Expand Down
5 changes: 5 additions & 0 deletions run/helloworld/ci-setup.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"env": {
"SERVICE_NAME": "run-helloworld-$RUN_ID"
}
}
4 changes: 3 additions & 1 deletion run/helloworld/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "c8 mocha -p -j 2 test/index.test.js --exit",
"test": "npm -- run all-test",
"all-test": "npm run unit-test && npm run system-test",
"unit-test": "c8 mocha -p -j 2 test/index.test.js --exit",
"system-test": "NAME=Cloud c8 mocha -p -j 2 test/system.test.js --timeout=180000"
},
"type": "module",
Expand Down
6 changes: 6 additions & 0 deletions run/helloworld/test/e2e_test_cleanup.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,9 @@ substitutions:
_VERSION: manual
_REGION: us-central1
_PLATFORM: managed
_SERVICE_ACCOUNT: ${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com

serviceAccount: 'projects/${PROJECT_ID}/serviceAccounts/${_SERVICE_ACCOUNT}'
options:
logging: CLOUD_LOGGING_ONLY
dynamicSubstitutions: true
10 changes: 8 additions & 2 deletions run/helloworld/test/e2e_test_setup.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ steps:
--no-allow-unauthenticated \
--region ${_REGION} \
--platform ${_PLATFORM} \
--set-env-vars NAME=${_NAME}"

--set-env-vars NAME=${_NAME} \
--add-custom-audiences 'https://action.test/'"

images:
- gcr.io/${PROJECT_ID}/${_SERVICE}:${_VERSION}
Expand All @@ -39,3 +39,9 @@ substitutions:
_REGION: us-central1
_PLATFORM: managed
_NAME: Cloud
_SERVICE_ACCOUNT: ${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com

serviceAccount: 'projects/${PROJECT_ID}/serviceAccounts/${_SERVICE_ACCOUNT}'
options:
logging: CLOUD_LOGGING_ONLY
dynamicSubstitutions: true
16 changes: 10 additions & 6 deletions run/helloworld/test/system.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
import assert from 'assert';
import {execSync} from 'child_process';
import request from 'got';
import {GoogleAuth} from 'google-auth-library';
const auth = new GoogleAuth();

const get = (route, base_url) => {
if (!ID_TOKEN) {
Expand All @@ -25,7 +23,7 @@ const get = (route, base_url) => {

return request(new URL(route, base_url.trim()), {
headers: {
Authorization: `${ID_TOKEN.trim()}`,
Authorization: `Bearer ${ID_TOKEN}`,
},
throwHttpErrors: false,
});
Expand All @@ -44,12 +42,17 @@ describe('End-to-End Tests', () => {
`"SERVICE_NAME" env var not found. Defaulting to "${SERVICE_NAME}"`
);
}
const {SERVICE_ACCOUNT} = process.env;
let {NAME} = process.env;
if (!NAME) {
NAME = 'Cloud';
console.log(`"NAME" env var not found. Defaulting to "${NAME}"`);
}
const {SAMPLE_VERSION} = process.env;
ID_TOKEN = process.env;
if (!ID_TOKEN) {
throw Error('"ID_TOKEN" env var not found.');
}
const PLATFORM = 'managed';
const REGION = 'us-central1';
before(async () => {
Expand All @@ -60,6 +63,7 @@ describe('End-to-End Tests', () => {
`--substitutions _SERVICE=${SERVICE_NAME},_PLATFORM=${PLATFORM},_REGION=${REGION}` +
`,_NAME=${NAME}`;
if (SAMPLE_VERSION) buildCmd += `,_VERSION=${SAMPLE_VERSION}`;
if (SERVICE_ACCOUNT) buildCmd += `,_SERVICE_ACCOUNT=${SERVICE_ACCOUNT}`;

console.log('Starting Cloud Build...');
execSync(buildCmd, {timeout: 240000}); // timeout at 4 mins
Expand All @@ -75,9 +79,8 @@ describe('End-to-End Tests', () => {
if (!BASE_URL) throw Error('Cloud Run service URL not found');

// Retrieve ID token for testing
const client = await auth.getIdTokenClient(BASE_URL);
const clientHeaders = await client.getRequestHeaders();
ID_TOKEN = clientHeaders['Authorization'].trim();
//const client = await auth.getIdTokenClient(BASE_URL);
//const clientHeaders = await client.getRequestHeaders();
if (!ID_TOKEN) throw Error('Unable to acquire an ID token.');
});

Expand All @@ -87,6 +90,7 @@ describe('End-to-End Tests', () => {
'--config ./test/e2e_test_cleanup.yaml ' +
`--substitutions _SERVICE=${SERVICE_NAME},_PLATFORM=${PLATFORM},_REGION=${REGION}`;
if (SAMPLE_VERSION) cleanUpCmd += `,_VERSION=${SAMPLE_VERSION}`;
if (SERVICE_ACCOUNT) cleanUpCmd += `,_SERVICE_ACCOUNT=${SERVICE_ACCOUNT}`;

execSync(cleanUpCmd);
});
Expand Down
Loading