-
Notifications
You must be signed in to change notification settings - Fork 964
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Apphosting emulator - environment variable support (#7781)
- Loading branch information
Showing
6 changed files
with
267 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import * as emulatorUtils from "../../utils"; | ||
import * as fsExtra from "fs-extra"; | ||
import * as path from "path"; | ||
import * as utils from "./utils"; | ||
|
||
import * as sinon from "sinon"; | ||
import { expect } from "chai"; | ||
import { getLocalAppHostingConfiguration, loadAppHostingYaml } from "./config"; | ||
|
||
const SAMPLE_APPHOSTING_YAML_CONFIG_ONE = { | ||
env: [ | ||
{ | ||
variable: "STORAGE_BUCKET", | ||
value: "mybucket.appspot.com", | ||
availability: ["BUILD", "RUNTIME"], | ||
}, | ||
{ | ||
variable: "API_KEY", | ||
secret: "myApiKeySecret", | ||
}, | ||
{ | ||
variable: "PINNED_API_KEY", | ||
secret: "myApiKeySecret@5", | ||
}, | ||
{ | ||
variable: "VERBOSE_API_KEY", | ||
secret: "projects/test-project/secrets/secretID", | ||
}, | ||
{ | ||
variable: "PINNED_VERBOSE_API_KEY", | ||
secret: "projects/test-project/secrets/secretID/versions/5", | ||
}, | ||
], | ||
}; | ||
|
||
const SAMPLE_APPHOSTING_YAML_CONFIG_TWO = { | ||
env: [ | ||
{ | ||
variable: "randomEnvOne", | ||
value: "envOne", | ||
}, | ||
{ | ||
variable: "randomEnvTwo", | ||
value: "envTwo", | ||
}, | ||
{ | ||
variable: "randomEnvThree", | ||
value: "envThree", | ||
}, | ||
{ variable: "randomSecretOne", secret: "secretOne" }, | ||
{ variable: "randomSecretTwo", secret: "secretTwo" }, | ||
{ variable: "randomSecretThree", secret: "secretThree" }, | ||
], | ||
}; | ||
|
||
const SAMPLE_APPHOSTING_YAML_CONFIG_THREE = { | ||
env: [ | ||
{ | ||
variable: "randomEnvOne", | ||
value: "envOne", | ||
}, | ||
{ | ||
variable: "randomEnvTwo", | ||
value: "blah", | ||
}, | ||
{ | ||
variable: "randomEnvFour", | ||
value: "envFour", | ||
}, | ||
{ variable: "randomSecretOne", secret: "bleh" }, | ||
{ variable: "randomSecretTwo", secret: "secretTwo" }, | ||
{ variable: "randomSecretFour", secret: "secretFour" }, | ||
], | ||
}; | ||
|
||
describe("environments", () => { | ||
let pathExistsStub: sinon.SinonStub; | ||
let joinStub: sinon.SinonStub; | ||
let loggerStub: sinon.SinonStub; | ||
let readFileFromDirectoryStub: sinon.SinonStub; | ||
let wrappedSafeLoadStub: sinon.SinonStub; | ||
|
||
beforeEach(() => { | ||
pathExistsStub = sinon.stub(fsExtra, "pathExists"); | ||
joinStub = sinon.stub(path, "join"); | ||
loggerStub = sinon.stub(utils, "logger"); | ||
readFileFromDirectoryStub = sinon.stub(emulatorUtils, "readFileFromDirectory"); | ||
wrappedSafeLoadStub = sinon.stub(emulatorUtils, "wrappedSafeLoad"); | ||
}); | ||
|
||
afterEach(() => { | ||
pathExistsStub.restore(); | ||
joinStub.restore(); | ||
loggerStub.restore(); | ||
readFileFromDirectoryStub.restore(); | ||
wrappedSafeLoadStub.restore(); | ||
}); | ||
|
||
describe("loadAppHostingYaml", () => { | ||
it("should return a configuration in the correct format", async () => { | ||
readFileFromDirectoryStub.returns({ source: "blah" }); | ||
wrappedSafeLoadStub.returns(SAMPLE_APPHOSTING_YAML_CONFIG_ONE); | ||
|
||
const res = await loadAppHostingYaml("test", "test.yaml"); | ||
expect(JSON.stringify(res)).to.equal( | ||
JSON.stringify({ | ||
environmentVariables: { | ||
STORAGE_BUCKET: "mybucket.appspot.com", | ||
}, | ||
secrets: { | ||
API_KEY: "myApiKeySecret", | ||
PINNED_API_KEY: "myApiKeySecret@5", | ||
VERBOSE_API_KEY: "projects/test-project/secrets/secretID", | ||
PINNED_VERBOSE_API_KEY: "projects/test-project/secrets/secretID/versions/5", | ||
}, | ||
}), | ||
); | ||
}); | ||
}); | ||
|
||
describe("getLocalAppHostingConfiguration", () => { | ||
it("should combine apphosting yaml files according to precedence", async () => { | ||
pathExistsStub.returns(true); | ||
readFileFromDirectoryStub.returns({ source: "blah" }); | ||
|
||
// Second config takes precedence | ||
wrappedSafeLoadStub.onFirstCall().returns(SAMPLE_APPHOSTING_YAML_CONFIG_TWO); | ||
wrappedSafeLoadStub.onSecondCall().returns(SAMPLE_APPHOSTING_YAML_CONFIG_THREE); | ||
|
||
const apphostingConfig = await getLocalAppHostingConfiguration("test"); | ||
|
||
expect(JSON.stringify(apphostingConfig)).to.equal( | ||
JSON.stringify({ | ||
environmentVariables: { | ||
randomEnvOne: "envOne", | ||
randomEnvTwo: "blah", | ||
randomEnvThree: "envThree", | ||
randomEnvFour: "envFour", | ||
}, | ||
secrets: { | ||
randomSecretOne: "bleh", | ||
randomSecretTwo: "secretTwo", | ||
randomSecretThree: "secretThree", | ||
randomSecretFour: "secretFour", | ||
}, | ||
}), | ||
); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { join } from "path"; | ||
import { pathExists } from "fs-extra"; | ||
import { readFileFromDirectory, wrappedSafeLoad } from "../../utils"; | ||
import { logger } from "./utils"; | ||
import { Emulators } from "../types"; | ||
|
||
type EnvironmentAvailability = "BUILD" | "RUNTIME"; | ||
|
||
const APPHOSTING_YAML = "apphosting.yaml"; | ||
const APPHOSTING_LOCAL_YAML = "apphosting.local.yaml"; | ||
|
||
// Schema of apphosting.*.yaml files | ||
interface AppHostingYaml { | ||
env?: { | ||
variable: string; | ||
secret?: string; | ||
value?: string; | ||
availability?: EnvironmentAvailability[]; | ||
}[]; | ||
} | ||
|
||
interface AppHostingConfiguration { | ||
environmentVariables?: Record<string, string>; | ||
secrets?: Record<string, string>; | ||
} | ||
|
||
/** | ||
* Reads an apphosting.*.yaml file, parses, and returns in an easy to use | ||
* format. | ||
*/ | ||
export async function loadAppHostingYaml( | ||
sourceDirectory: string, | ||
fileName: string, | ||
): Promise<AppHostingConfiguration> { | ||
const file = await readFileFromDirectory(sourceDirectory, fileName); | ||
const apphostingYaml: AppHostingYaml = await wrappedSafeLoad(file.source); | ||
|
||
const environmentVariables: Record<string, string> = {}; | ||
const secrets: Record<string, string> = {}; | ||
|
||
if (apphostingYaml.env) { | ||
for (const env of apphostingYaml.env) { | ||
if (env.value) { | ||
environmentVariables[env.variable] = env.value; | ||
} | ||
|
||
if (env.secret) { | ||
secrets[env.variable] = env.secret; | ||
} | ||
} | ||
} | ||
|
||
return { environmentVariables, secrets }; | ||
} | ||
|
||
/** | ||
* Loads in apphosting.yaml & apphosting.local.yaml, giving | ||
* apphosting.local.yaml precedence if present. | ||
*/ | ||
export async function getLocalAppHostingConfiguration( | ||
sourceDirectory: string, | ||
): Promise<AppHostingConfiguration> { | ||
let apphostingBaseConfig: AppHostingConfiguration = {}; | ||
let apphostingLocalConfig: AppHostingConfiguration = {}; | ||
|
||
if (await pathExists(join(sourceDirectory, APPHOSTING_YAML))) { | ||
logger.logLabeled( | ||
"SUCCESS", | ||
Emulators.APPHOSTING, | ||
`${APPHOSTING_YAML} found, loading configuration`, | ||
); | ||
apphostingBaseConfig = await loadAppHostingYaml(sourceDirectory, APPHOSTING_YAML); | ||
} | ||
|
||
if (await pathExists(join(sourceDirectory, APPHOSTING_LOCAL_YAML))) { | ||
logger.logLabeled( | ||
"SUCCESS", | ||
Emulators.APPHOSTING, | ||
`${APPHOSTING_LOCAL_YAML} found, loading configuration`, | ||
); | ||
apphostingLocalConfig = await loadAppHostingYaml(sourceDirectory, APPHOSTING_LOCAL_YAML); | ||
} | ||
|
||
// Combine apphosting configurations in order of lowest precedence to highest | ||
return { | ||
environmentVariables: { | ||
...apphostingBaseConfig.environmentVariables, | ||
...apphostingLocalConfig.environmentVariables, | ||
}, | ||
secrets: { | ||
...apphostingBaseConfig.secrets, | ||
...apphostingLocalConfig.secrets, | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters