Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,11 @@ export class ArgoCDDatastoreService {
select: projectSelect,
})
}

async getAllZoneSlugs(): Promise<string[]> {
const zones = await this.prisma.zone.findMany({
select: { slug: true },
})
return zones.map(zone => zone.slug)
}
}
49 changes: 49 additions & 0 deletions apps/server-nestjs/src/modules/argocd/argocd.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ describe('argoCDService', () => {
deployVaultConnectionInNamespaces: false,
})

datastore.getAllZoneSlugs.mockResolvedValue(['zone-1'])

const module = await Test.createTestingModule({
providers: [
ArgoCDService,
Expand Down Expand Up @@ -352,6 +354,53 @@ describe('argoCDService', () => {
expect(gitlab.generateCreateOrUpdateAction).toHaveBeenCalledTimes(1)
})

it('should delete leftover values files when the project has no environments', async () => {
const mockProject = makeProjectWithDetails({
slug: 'project-1',
name: 'Project 1',
environments: [],
repositories: [makeProjectRepository({ internalRepoName: 'infra-repo', isInfra: true })],
deployments: [],
})

const infraProject = makeProjectSchema({ id: 100, http_url_to_repo: 'https://gitlab.internal/infra' })
datastore.getAllProjects.mockResolvedValue([mockProject])
// The project no longer has any environment, so the zone must be discovered
// from the platform zone list rather than from the project's environments.
datastore.getAllZoneSlugs.mockResolvedValue(['zone-1'])
gitlab.getOrCreateInfraGroupRepo.mockResolvedValue(infraProject)
gitlab.getOrCreateProjectGroupPublicUrl.mockResolvedValue('https://gitlab.internal/group')
gitlab.getOrCreateInfraGroupRepoPublicUrl.mockResolvedValue('https://gitlab.internal/infra-repo')
gitlab.listFiles.mockResolvedValue([
makeRepositoryTreeSchema(
{ name: 'values.yaml', path: 'Project 1/cluster-1/dev/values.yaml' },
),
makeRepositoryTreeSchema(
{ name: 'values.yaml', path: 'Project 1/cluster-1/prod/values.yaml' },
),
])

await expect(service.handleCron()).resolves.not.toThrow()

expect(gitlab.maybeCreateCommit).toHaveBeenCalledTimes(1)
expect(gitlab.maybeCreateCommit).toHaveBeenCalledWith(
infraProject,
'ci: :robot_face: Sync project-1',
expect.arrayContaining([
{
action: 'delete',
filePath: 'Project 1/cluster-1/dev/values.yaml',
},
{
action: 'delete',
filePath: 'Project 1/cluster-1/prod/values.yaml',
},
]),
)

expect(gitlab.generateCreateOrUpdateAction).not.toHaveBeenCalled()
})

it('should not commit when there is no diff', async () => {
const mockProject = makeProjectWithDetails({
slug: 'project-1',
Expand Down
19 changes: 3 additions & 16 deletions apps/server-nestjs/src/modules/argocd/argocd.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ export class ArgoCDService {
): Promise<void> {
const span = trace.getActiveSpan()
span?.setAttribute('project.slug', project.slug)
const zones = getDistinctZones(project)
// Visit every zone, not only those with a current environment, so leftover
// values files are purged from zones the project no longer deploys to.
Comment thread
KepoParis marked this conversation as resolved.
const zones = await this.argoCDDatastore.getAllZoneSlugs()
span?.setAttribute('argocd.zones.count', zones.length)
this.logger.verbose(`Reconciling ArgoCD zones for project ${project.slug} (count=${zones.length})`)
await Promise.all(zones.map(zoneSlug => this.ensureZone(project, zoneSlug)))
Expand Down Expand Up @@ -167,11 +169,6 @@ export class ArgoCDService {
zoneSlug: string,
): Promise<CommitAction[]> {
const neededFiles = new Set<string>()
const clusterLabelsInZone = new Set(
project.environments
.filter(e => e.cluster.zone.slug === zoneSlug)
.map(e => e.cluster.label),
)

project.environments.forEach((env) => {
if (env.cluster?.zone.slug !== zoneSlug) return
Expand All @@ -189,10 +186,6 @@ export class ArgoCDService {
if (existingFile.name !== 'values.yaml') return false
if (!existingFile.path.startsWith(projectPrefix)) return false

const remaining = existingFile.path.slice(projectPrefix.length)
const clusterLabel = remaining.split('/')[0]
if (!clusterLabel || !clusterLabelsInZone.has(clusterLabel)) return false

return !neededFiles.has(existingFile.path)
})
.map(existingFile => ({ action: 'delete', filePath: existingFile.path } satisfies CommitAction))
Expand Down Expand Up @@ -445,12 +438,6 @@ function formatEnvironmentValuesFilePath(project: { name: string }, cluster: { l
return `${project.name}/${cluster.label}/${env.name}/values.yaml`
}

function getDistinctZones(project: ProjectWithDetails) {
const zones = new Set<string>()
project.environments.forEach(e => zones.add(e.cluster.zone.slug))
return [...zones]
}

function splitExtraRepositories(extraRepositories: string | undefined): string[] {
if (!extraRepositories) return []
return extraRepositories.split(',').map(r => r.trim()).filter(r => r.length > 0)
Expand Down