mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: Add environment variable to set override enabled environments
This commit is contained in:
		
							parent
							
								
									3910e23d2d
								
							
						
					
					
						commit
						c3b064adfc
					
				| @ -224,3 +224,47 @@ test('should handle cases where no env var specified for tokens', async () => { | ||||
| 
 | ||||
|     expect(config.authentication.initApiTokens).toHaveLength(1); | ||||
| }); | ||||
| 
 | ||||
| test('should load environment overrides from env var', async () => { | ||||
|     process.env.ENABLED_ENVIRONMENTS = 'default,production'; | ||||
| 
 | ||||
|     const config = createConfig({ | ||||
|         db: { | ||||
|             host: 'localhost', | ||||
|             port: 4242, | ||||
|             user: 'unleash', | ||||
|             password: 'password', | ||||
|             database: 'unleash_db', | ||||
|         }, | ||||
|         server: { | ||||
|             port: 4242, | ||||
|         }, | ||||
|         authentication: { | ||||
|             initApiTokens: [], | ||||
|         }, | ||||
|     }); | ||||
| 
 | ||||
|     expect(config.environmentEnableOverrides).toHaveLength(2); | ||||
|     expect(config.environmentEnableOverrides).toContain('production'); | ||||
|     delete process.env.ENABLED_ENVIRONMENTS; | ||||
| }); | ||||
| 
 | ||||
| test('should yield an empty list when no environment overrides are specified', async () => { | ||||
|     const config = createConfig({ | ||||
|         db: { | ||||
|             host: 'localhost', | ||||
|             port: 4242, | ||||
|             user: 'unleash', | ||||
|             password: 'password', | ||||
|             database: 'unleash_db', | ||||
|         }, | ||||
|         server: { | ||||
|             port: 4242, | ||||
|         }, | ||||
|         authentication: { | ||||
|             initApiTokens: [], | ||||
|         }, | ||||
|     }); | ||||
| 
 | ||||
|     expect(config.environmentEnableOverrides).toStrictEqual([]); | ||||
| }); | ||||
|  | ||||
| @ -217,6 +217,14 @@ const loadInitApiTokens = () => { | ||||
|     ]; | ||||
| }; | ||||
| 
 | ||||
| const loadEnvironmentEnableOverrides = () => { | ||||
|     const environmentsString = process.env.ENABLED_ENVIRONMENTS; | ||||
|     if (environmentsString) { | ||||
|         return environmentsString.split(','); | ||||
|     } | ||||
|     return []; | ||||
| }; | ||||
| 
 | ||||
| export function createConfig(options: IUnleashOptions): IUnleashConfig { | ||||
|     let extraDbOptions = {}; | ||||
| 
 | ||||
| @ -275,6 +283,8 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig { | ||||
|         { initApiTokens: initApiTokens }, | ||||
|     ]); | ||||
| 
 | ||||
|     const environmentEnableOverrides = loadEnvironmentEnableOverrides(); | ||||
| 
 | ||||
|     const importSetting: IImportOption = mergeAll([ | ||||
|         defaultImport, | ||||
|         options.import, | ||||
| @ -323,6 +333,7 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig { | ||||
|         eventHook: options.eventHook, | ||||
|         enterpriseVersion: options.enterpriseVersion, | ||||
|         eventBus: new EventEmitter(), | ||||
|         environmentEnableOverrides, | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -163,6 +163,22 @@ export default class EnvironmentStore implements IEnvironmentStore { | ||||
|         return mapRow(row[0]); | ||||
|     } | ||||
| 
 | ||||
|     async disableAllExcept(environments: string[]): Promise<void> { | ||||
|         await this.db(TABLE) | ||||
|             .update({ | ||||
|                 enabled: false, | ||||
|             }) | ||||
|             .whereNotIn('name', environments); | ||||
|     } | ||||
| 
 | ||||
|     async enable(environments: string[]): Promise<void> { | ||||
|         await this.db(TABLE) | ||||
|             .update({ | ||||
|                 enabled: true, | ||||
|             }) | ||||
|             .whereIn('name', environments); | ||||
|     } | ||||
| 
 | ||||
|     async delete(name: string): Promise<void> { | ||||
|         await this.db(TABLE).where({ name, protected: false }).del(); | ||||
|     } | ||||
|  | ||||
| @ -87,6 +87,12 @@ async function createApp( | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     if (config.environmentEnableOverrides?.length > 0) { | ||||
|         await services.environmentService.overrideEnabledProjects( | ||||
|             config.environmentEnableOverrides, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     return new Promise((resolve, reject) => { | ||||
|         if (startApp) { | ||||
|             const server = stoppable( | ||||
|  | ||||
| @ -94,6 +94,28 @@ export default class EnvironmentService { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async overrideEnabledProjects( | ||||
|         environmentsToEnable: string[], | ||||
|     ): Promise<void> { | ||||
|         if (environmentsToEnable.length === 0) { | ||||
|             return Promise.resolve(); | ||||
|         } | ||||
| 
 | ||||
|         const environmentsExist = await Promise.all( | ||||
|             environmentsToEnable.map((env) => | ||||
|                 this.environmentStore.exists(env), | ||||
|             ), | ||||
|         ); | ||||
|         if (!environmentsExist.every((exists) => exists)) { | ||||
|             this.logger.error( | ||||
|                 "Found environment enabled overrides but some of the specified environments don't exist, no overrides will be executed", | ||||
|             ); | ||||
|             return; | ||||
|         } | ||||
|         await this.environmentStore.disableAllExcept(environmentsToEnable); | ||||
|         await this.environmentStore.enable(environmentsToEnable); | ||||
|     } | ||||
| 
 | ||||
|     async removeEnvironmentFromProject( | ||||
|         environment: string, | ||||
|         projectId: string, | ||||
|  | ||||
| @ -158,4 +158,5 @@ export interface IUnleashConfig { | ||||
|     enterpriseVersion?: string; | ||||
|     eventBus: EventEmitter; | ||||
|     disableLegacyFeaturesApi?: boolean; | ||||
|     environmentEnableOverrides?: string[]; | ||||
| } | ||||
|  | ||||
| @ -16,4 +16,6 @@ export interface IEnvironmentStore extends Store<IEnvironment, string> { | ||||
|     updateSortOrder(id: string, value: number): Promise<void>; | ||||
|     importEnvironments(environments: IEnvironment[]): Promise<IEnvironment[]>; | ||||
|     delete(name: string): Promise<void>; | ||||
|     disableAllExcept(environments: string[]): Promise<void>; | ||||
|     enable(environments: string[]): Promise<void>; | ||||
| } | ||||
|  | ||||
| @ -136,3 +136,56 @@ test('Trying to get an environment that does not exist throws NotFoundError', as | ||||
|         new NotFoundError(`Could not find environment with name: ${envName}`), | ||||
|     ); | ||||
| }); | ||||
| 
 | ||||
| test('Setting an override disables all other envs', async () => { | ||||
|     const enabledEnvName = 'should-get-enabled'; | ||||
|     const disabledEnvName = 'should-get-disabled'; | ||||
|     await db.stores.environmentStore.create({ | ||||
|         name: disabledEnvName, | ||||
|         type: 'production', | ||||
|     }); | ||||
| 
 | ||||
|     await db.stores.environmentStore.create({ | ||||
|         name: enabledEnvName, | ||||
|         type: 'production', | ||||
|     }); | ||||
| 
 | ||||
|     //Set these to the wrong state so we can assert that overriding them flips
 | ||||
|     await service.toggleEnvironment(disabledEnvName, true); | ||||
|     await service.toggleEnvironment(enabledEnvName, false); | ||||
| 
 | ||||
|     await service.overrideEnabledProjects([enabledEnvName]); | ||||
| 
 | ||||
|     const environments = await service.getAll(); | ||||
|     const targetedEnvironment = environments.find( | ||||
|         (env) => env.name == enabledEnvName, | ||||
|     ); | ||||
| 
 | ||||
|     const allOtherEnvironments = environments | ||||
|         .filter((x) => x.name != enabledEnvName) | ||||
|         .map((env) => env.enabled); | ||||
| 
 | ||||
|     console.log(allOtherEnvironments); | ||||
|     expect(targetedEnvironment.enabled).toBe(true); | ||||
|     expect(allOtherEnvironments.every((x) => x === false)).toBe(true); | ||||
| }); | ||||
| 
 | ||||
| test('Passing an empty override does nothing', async () => { | ||||
|     const enabledEnvName = 'should-be-enabled'; | ||||
| 
 | ||||
|     await db.stores.environmentStore.create({ | ||||
|         name: enabledEnvName, | ||||
|         type: 'production', | ||||
|     }); | ||||
| 
 | ||||
|     await service.toggleEnvironment(enabledEnvName, true); | ||||
| 
 | ||||
|     await service.overrideEnabledProjects([]); | ||||
| 
 | ||||
|     const environments = await service.getAll(); | ||||
|     const targetedEnvironment = environments.find( | ||||
|         (env) => env.name == enabledEnvName, | ||||
|     ); | ||||
| 
 | ||||
|     expect(targetedEnvironment.enabled).toBe(true); | ||||
| }); | ||||
|  | ||||
							
								
								
									
										14
									
								
								src/test/fixtures/fake-environment-store.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								src/test/fixtures/fake-environment-store.ts
									
									
									
									
										vendored
									
									
								
							| @ -10,6 +10,20 @@ export default class FakeEnvironmentStore implements IEnvironmentStore { | ||||
| 
 | ||||
|     environments: IEnvironment[] = []; | ||||
| 
 | ||||
|     disableAllExcept(environments: string[]): Promise<void> { | ||||
|         for (let env of this.environments) { | ||||
|             if (!environments.includes(env.name)) env.enabled = false; | ||||
|         } | ||||
|         return Promise.resolve(); | ||||
|     } | ||||
| 
 | ||||
|     enable(environments: string[]): Promise<void> { | ||||
|         for (let env of this.environments) { | ||||
|             if (environments.includes(env.name)) env.enabled = true; | ||||
|         } | ||||
|         return Promise.resolve(); | ||||
|     } | ||||
| 
 | ||||
|     async getAll(): Promise<IEnvironment[]> { | ||||
|         return this.environments; | ||||
|     } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user