diff --git a/ENVIRONMENTS.md b/ENVIRONMENTS.md index 5bf9b227b6..6c8cd9c397 100644 --- a/ENVIRONMENTS.md +++ b/ENVIRONMENTS.md @@ -240,6 +240,7 @@ pnpm docker:dev | `ARGOCD_URL` | — | URL publique ArgoCD | | `ARGOCD_INTERNAL_URL` | — | URL interne ArgoCD | | `ARGOCD_EXTRA_REPOSITORIES` | — | Répôts additionnels (JSON) | +| `ARGOCD_SHARED_SOURCE_REPOSITORIES` | — | Sources autorisées pour tous les projets, `` remplacé par le slug (ex. `oci://registry.example.com//**`) | ### Variables GitLab diff --git a/apps/server-nestjs/.env.integ-example b/apps/server-nestjs/.env.integ-example index c8c051618a..a56c2ff131 100644 --- a/apps/server-nestjs/.env.integ-example +++ b/apps/server-nestjs/.env.integ-example @@ -30,6 +30,10 @@ ARGO_NAMESPACE= ARGOCD_URL= # URL interne de l'API ArgoCD ARGOCD_INTERNAL_URL= +# Sources autorisées pour tous les projets, séparées par des virgules. +# est remplacé par le slug du projet. +# Ex. pour le registre Harbor : oci://registry.example.com//** +ARGOCD_SHARED_SOURCE_REPOSITORIES= # --- GitLab --- # Token d'accès personnel GitLab (scope api) diff --git a/apps/server-nestjs/src/config/argocd.config.spec.ts b/apps/server-nestjs/src/config/argocd.config.spec.ts index 04778de631..476c39188d 100644 --- a/apps/server-nestjs/src/config/argocd.config.spec.ts +++ b/apps/server-nestjs/src/config/argocd.config.spec.ts @@ -3,7 +3,7 @@ import { argocdConfigFactory } from './argocd.config' import { resetEnvs } from './config-testing.utils' describe('argocdConfig', () => { - beforeEach(() => { resetEnvs(['ARGO_NAMESPACE', 'ARGOCD_URL', 'ARGOCD_INTERNAL_URL', 'DSO_ENV_CHART_VERSION', 'DSO_NS_CHART_VERSION', 'VAULT__DEPLOY_VAULT_CONNECTION_IN_NS']) }) + beforeEach(() => { resetEnvs(['ARGO_NAMESPACE', 'ARGOCD_URL', 'ARGOCD_INTERNAL_URL', 'DSO_ENV_CHART_VERSION', 'DSO_NS_CHART_VERSION', 'VAULT__DEPLOY_VAULT_CONNECTION_IN_NS', 'ARGOCD_SHARED_SOURCE_REPOSITORIES']) }) afterEach(() => { vi.unstubAllEnvs() }) it('parses a full config', () => { @@ -46,6 +46,20 @@ describe('argocdConfig', () => { expect(argocdConfigFactory().extraRepositories).toEqual([]) }) + it('parses shared source repositories', () => { + vi.stubEnv('ARGOCD_URL', 'https://argocd.internal') + vi.stubEnv('ARGOCD_SHARED_SOURCE_REPOSITORIES', 'oci://registry.internal//**,https://charts.internal/**') + expect(argocdConfigFactory().sharedSourceRepositories).toEqual([ + 'oci://registry.internal//**', + 'https://charts.internal/**', + ]) + }) + + it('defaults sharedSourceRepositories to an empty array', () => { + vi.stubEnv('ARGOCD_URL', 'https://argocd.internal') + expect(argocdConfigFactory().sharedSourceRepositories).toEqual([]) + }) + it('throws when a required var is missing', () => { expect(() => argocdConfigFactory()).toThrow() }) diff --git a/apps/server-nestjs/src/config/argocd.config.ts b/apps/server-nestjs/src/config/argocd.config.ts index ab6d413109..ff219bcbcd 100644 --- a/apps/server-nestjs/src/config/argocd.config.ts +++ b/apps/server-nestjs/src/config/argocd.config.ts @@ -7,6 +7,7 @@ const argocdFeatureSchema = z.object({ ARGOCD_URL: z.string().url(), ARGOCD_INTERNAL_URL: z.string().url().optional(), ARGOCD_EXTRA_REPOSITORIES: csv(z.string()), + ARGOCD_SHARED_SOURCE_REPOSITORIES: csv(z.string()), DSO_ENV_CHART_VERSION: z.string().default('dso-env-1.6.0'), DSO_NS_CHART_VERSION: z.string().default('dso-ns-1.1.5'), VAULT__DEPLOY_VAULT_CONNECTION_IN_NS: truthySchema.default('false').transform(v => v === 'true' || v === '1'), @@ -15,6 +16,7 @@ const argocdFeatureSchema = z.object({ url: raw.ARGOCD_URL, internalUrl: raw.ARGOCD_INTERNAL_URL, extraRepositories: raw.ARGOCD_EXTRA_REPOSITORIES, + sharedSourceRepositories: raw.ARGOCD_SHARED_SOURCE_REPOSITORIES, dsoEnvChartVersion: raw.DSO_ENV_CHART_VERSION, dsoNsChartVersion: raw.DSO_NS_CHART_VERSION, vaultDeployVaultConnectionInNs: raw.VAULT__DEPLOY_VAULT_CONNECTION_IN_NS, diff --git a/apps/server-nestjs/src/modules/argocd/argocd.module.ts b/apps/server-nestjs/src/modules/argocd/argocd.module.ts index e38fad5b8b..8e2f3f38e6 100644 --- a/apps/server-nestjs/src/modules/argocd/argocd.module.ts +++ b/apps/server-nestjs/src/modules/argocd/argocd.module.ts @@ -13,7 +13,15 @@ import { ArgoCDPluginService } from './argocd-plugin.service' import { ArgoCDService } from './argocd.service' @Module({ - imports: [DatabaseModule, GitlabModule, TerminusModule, VaultModule, ConfigModule.forFeature(argocdConfigFactory), ConfigModule.forFeature(vaultConfigFactory), ConfigModule.forFeature(baseConfigFactory)], + imports: [ + DatabaseModule, + GitlabModule, + TerminusModule, + VaultModule, + ConfigModule.forFeature(argocdConfigFactory), + ConfigModule.forFeature(vaultConfigFactory), + ConfigModule.forFeature(baseConfigFactory), + ], providers: [ArgoCDHealthService, ArgoCDPluginService, ArgoCDService, ArgoCDDatastoreService], exports: [ArgoCDHealthService, ArgoCDPluginService, ArgoCDService], }) diff --git a/apps/server-nestjs/src/modules/argocd/argocd.service.spec.ts b/apps/server-nestjs/src/modules/argocd/argocd.service.spec.ts index 1f88282ada..5eddeef014 100644 --- a/apps/server-nestjs/src/modules/argocd/argocd.service.spec.ts +++ b/apps/server-nestjs/src/modules/argocd/argocd.service.spec.ts @@ -41,6 +41,7 @@ describe('argoCDService', () => { url: 'https://argocd.internal', internalUrl: undefined, extraRepositories: ['repo3'], + sharedSourceRepositories: ['oci://harbor.internal//**'], dsoEnvChartVersion: 'dso-env-1.6.0', dsoNsChartVersion: 'dso-ns-1.1.5', vaultDeployVaultConnectionInNs: false, @@ -302,6 +303,7 @@ describe('argoCDService', () => { }, sourceRepositories: [ 'https://gitlab.internal/group/project-1/**', + 'oci://harbor.internal/project-1/**', 'repo2', ], destination: { @@ -377,6 +379,7 @@ describe('argoCDService', () => { }, sourceRepositories: [ 'https://gitlab.internal/group/project-1/**', + 'oci://harbor.internal/project-1/**', 'repo2', ], destination: { @@ -637,6 +640,7 @@ describe('argoCDService', () => { }, sourceRepositories: [ 'https://gitlab.internal/group/project-1/**', + 'oci://harbor.internal/project-1/**', 'repo2', ], destination: { @@ -817,4 +821,44 @@ describe('argoCDService', () => { expect(values.application.repositories[0].valueFiles).toStrictEqual(['values-.yaml']) }) + + it('should omit shared source repositories when none are configured', async () => { + argocdConfig.sharedSourceRepositories = [] + const mockProject = makeProjectWithDetails({ + slug: 'project-1', + name: 'Project 1', + environments: [ + makeProjectEnvironment({ name: 'dev', cluster: { id: 'c1', label: 'cluster-1', zone: { slug: 'zone-1' } } }), + ], + repositories: [], + plugins: [{ pluginName: 'argocd', key: 'extraRepositories', value: 'repo2' }], + deployments: [], + }) + + const infraProject = makeProjectSchema({ id: 100, http_url_to_repo: 'https://gitlab.internal/infra' }) + datastore.getAllProjects.mockResolvedValue([mockProject]) + gitlab.getOrCreateInfraGroupRepo.mockResolvedValue(infraProject) + gitlab.getOrCreateProjectGroupPublicUrl.mockResolvedValue('https://gitlab.internal/group') + gitlab.getOrCreateInfraGroupRepoPublicUrl.mockResolvedValue('https://gitlab.internal/infra-repo') + gitlab.listFiles.mockResolvedValue([]) + vault.getAuthApproleRoleRoleId.mockResolvedValue('role-id') + vault.createAuthApproleRoleSecretId.mockResolvedValue('secret-id') + gitlab.generateCreateOrUpdateAction.mockImplementation(async (_repoId, _ref, filePath: string, content: string) => { + return makeCommitAction({ filePath, content }) + }) + + await expect(service.handleCron()).resolves.not.toThrow() + + const actions = gitlab.maybeCreateCommit.mock.calls[0][2] + const values = actions + .filter((action): action is typeof action & { content: string } => 'content' in action) + .map(action => parse(action.content)) + .find(v => v.application?.sourceRepositories) + expect(values).toBeDefined() + + expect(values.application.sourceRepositories).toStrictEqual([ + 'https://gitlab.internal/group/project-1/**', + 'repo2', + ]) + }) }) diff --git a/apps/server-nestjs/src/modules/argocd/argocd.service.ts b/apps/server-nestjs/src/modules/argocd/argocd.service.ts index bc8aeb1716..9a371d9d50 100644 --- a/apps/server-nestjs/src/modules/argocd/argocd.service.ts +++ b/apps/server-nestjs/src/modules/argocd/argocd.service.ts @@ -306,6 +306,7 @@ export class ArgoCDService { environment, cluster, gitlabPublicProjectUrl, + sharedSourceRepositories: formatSharedSourceRepositories(this.argocdConfig.sharedSourceRepositories, project.slug), argocdExtraRepositories: this.getExtraRepositories(project), infraProject, valueFilePath, @@ -378,6 +379,7 @@ export class ArgoCDService { environment, cluster, gitlabPublicProjectUrl, + sharedSourceRepositories: formatSharedSourceRepositories(this.argocdConfig.sharedSourceRepositories, project.slug), argocdExtraRepositories: this.getExtraRepositories(project), infraProject, valueFilePath, @@ -611,14 +613,20 @@ function formatEnvironmentValues( interface FormatSourceRepositoriesValuesOptions { gitlabPublicProjectUrl: string + sharedSourceRepositories: string[] argocdExtraRepositories?: string[] } +function formatSharedSourceRepositories(sharedSourceRepositories: string[], projectSlug: string): string[] { + return sharedSourceRepositories.map(repository => repository.replaceAll('', projectSlug)) +} + function formatSourceRepositoriesValues( - { gitlabPublicProjectUrl, argocdExtraRepositories = [] }: FormatSourceRepositoriesValuesOptions, + { gitlabPublicProjectUrl, sharedSourceRepositories, argocdExtraRepositories = [] }: FormatSourceRepositoriesValuesOptions, ): string[] { return [ `${gitlabPublicProjectUrl}/**`, + ...sharedSourceRepositories, ...argocdExtraRepositories, ] } @@ -661,6 +669,7 @@ interface FormatValuesOptions { environment: ProjectWithDetails['environments'][number] cluster: ProjectWithDetails['environments'][number]['cluster'] gitlabPublicProjectUrl: string + sharedSourceRepositories: string[] argocdExtraRepositories?: string[] vaultValues: Record infraProject: SimpleProjectSchema @@ -676,6 +685,7 @@ function formatValues({ environment, cluster, gitlabPublicProjectUrl, + sharedSourceRepositories, argocdExtraRepositories, vaultValues, infraProject, @@ -708,6 +718,7 @@ function formatValues({ }, sourceRepositories: formatSourceRepositoriesValues({ gitlabPublicProjectUrl, + sharedSourceRepositories, argocdExtraRepositories, }), destination: {