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

feat: gitea org for each team #163

Merged
merged 28 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8f2a9cf
feat: create org for every team
ElderMatt Dec 17, 2024
47f29af
fix: disable test for gitea
ElderMatt Dec 17, 2024
a288292
fix: test
ElderMatt Dec 17, 2024
093e850
fix: group map string builder
ElderMatt Dec 17, 2024
05b147d
fix: undefined error in existingteams
ElderMatt Dec 17, 2024
ba53cf0
fix: isempty check and disable initial repo for teams
ElderMatt Dec 17, 2024
57a92d4
fix: creating organizations
ElderMatt Dec 18, 2024
bcd50d6
fix: org is empty check
ElderMatt Dec 19, 2024
3f658b2
fix: streamline isempty check
ElderMatt Dec 19, 2024
d88d221
fix: create otomi-viewer team again
ElderMatt Dec 19, 2024
1220afa
fix: added more checks
ElderMatt Dec 19, 2024
3d38add
feat: get teams from org seperate error
ElderMatt Dec 19, 2024
2aa6a17
feat: remove old code
ElderMatt Dec 19, 2024
df0ff7a
Merge branch 'main' into apl-430
CasLubbers Dec 23, 2024
f274e20
fix: use username in create org
ElderMatt Jan 2, 2025
cbafa32
fix: username set to organization name
ElderMatt Jan 2, 2025
daf0272
fix: add in teamid otomi
ElderMatt Jan 2, 2025
44b654e
fix: group mapping
ElderMatt Jan 3, 2025
8ea2182
fix: create teams and group mapping
ElderMatt Jan 3, 2025
dbac3d9
feat: filter out otomi from team ids
ElderMatt Jan 3, 2025
07c9461
feat: dont add teams to otomi org
ElderMatt Jan 3, 2025
0c2544c
fix: changed not include to not equals instead
ElderMatt Jan 3, 2025
6738aba
fix: added back argocd and team repos in otomi
ElderMatt Jan 6, 2025
eb568d7
Merge branch 'main' into apl-430
ElderMatt Jan 7, 2025
e2fb1e3
fix: change back launch and tasks json
ElderMatt Jan 7, 2025
8423fda
fix: removed comments
ElderMatt Jan 9, 2025
bebd165
fix: removed unused code and renamed function
ElderMatt Jan 10, 2025
3607ed8
Merge branch 'main' into apl-430
Ani1357 Jan 10, 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
2 changes: 1 addition & 1 deletion src/operator/gitea.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('giteaOperator', () => {
it('should create a valid group mapping string with all the teams', () => {
const mappingString = buildTeamString(teamNames)
expect(mappingString).to.be.equal(
'{"platform-admin":{"otomi":["Owners"]},"team-demo":{"otomi":["otomi-viewer","team-demo"]},"team-demo2":{"otomi":["otomi-viewer","team-demo2"]},"team-demo3":{"otomi":["otomi-viewer","team-demo3"]}}',
'{"platform-admin":{"otomi":["Owners"]},"team-demo":{"otomi":["otomi-viewer","team-demo"],"demo":["owners"]},"team-demo2":{"otomi":["otomi-viewer","team-demo2"],"demo2":["owners"]},"team-demo3":{"otomi":["otomi-viewer","team-demo3"],"demo3":["owners"]}}',
)
expect(mappingString).to.not.contain('team-admin')
})
Expand Down
150 changes: 92 additions & 58 deletions src/operator/gitea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import {
CreateRepoOption,
CreateTeamOption,
EditRepoOption,
Organization,
OrganizationApi,
Repository,
RepositoryApi,
Team,
} from '@linode/gitea-client-node'
import { keys } from 'lodash'
import { isEmpty, keys } from 'lodash'
import { doApiCall } from '../utils'
import {
CHECK_OIDC_CONFIG_INTERVAL,
Expand All @@ -32,7 +33,7 @@ interface hookInfo {

interface groupMapping {
[key: string]: {
otomi: string[]
[teamId: string]: string[]
}
}

Expand Down Expand Up @@ -156,7 +157,6 @@ const createSetGiteaOIDCConfig = (() => {

// Operator
export default class MyOperator extends Operator {
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
protected async init() {
// Run setGiteaOIDCConfig every 30 seconds
createSetGiteaOIDCConfig()
Expand Down Expand Up @@ -242,37 +242,65 @@ async function runSetupGitea() {
}
}

async function upsertOrganization(
orgApi: OrganizationApi,
existingOrganizations: Organization[],
organizationName: string,
): Promise<void> {
const orgOption = {
...new CreateOrgOption(),
username: organizationName,
fullName: organizationName,
repoAdminChangeTeamAccess: true,
}
const existingOrg = existingOrganizations.find((organization) => organization.name === organizationName)
if (isEmpty(existingOrg))
return doApiCall(errors, `Creating org "${orgOption.fullName}"`, () => orgApi.orgCreate(orgOption), 422)

return doApiCall(
errors,
`Updating org "${orgOption.fullName}"`,
() => orgApi.orgEdit(organizationName, orgOption),
422,
)
}

// Setup Gitea Functions
async function upsertTeam(
existingTeams: Team[] = [],
orgApi: OrganizationApi,
organizationName: string,
teamOption: CreateTeamOption,
): Promise<void> {
const existingTeam = existingTeams.find((el) => el.name === teamOption.name)
if (existingTeam)
const getErrors: string[] = []
const existingTeams: Team[] = await doApiCall(getErrors, `Getting all teams in org "${organizationName}"`, () =>
orgApi.orgListTeams(organizationName),
)
if (!isEmpty(getErrors)) console.error('Errors when gettings teams.', getErrors)
const existingTeam = existingTeams?.find((team) => team.name === teamOption.name)
if (isEmpty(existingTeam))
return doApiCall(
errors,
`Updating team "${teamOption.name}" in org "${orgName}"`,
() => orgApi.orgEditTeam(existingTeam.id!, teamOption),
`Creating team "${teamOption.name}" in org "${organizationName}"`,
() => orgApi.orgCreateTeam(organizationName, teamOption),
422,
)
return doApiCall(
errors,
`Updating team "${teamOption.name}" in org "${orgName}"`,
() => orgApi.orgCreateTeam(orgName, teamOption),
`Updating team "${teamOption.name}" in org "${organizationName}"`,
() => orgApi.orgEditTeam(existingTeam!.id!, teamOption),
422,
)
}

async function upsertRepo(
existingRepos: Repository[] = [],
existingReposInOrg: Repository[] = [],
orgApi: OrganizationApi,
repoApi: RepositoryApi,
repoOption: CreateRepoOption | EditRepoOption,
teamName?: string,
): Promise<void> {
const existingRepo = existingRepos.find((el) => el.name === repoOption.name)
if (!existingRepo) {
const existingRepo = existingReposInOrg.find((repository) => repository.name === repoOption.name)
if (isEmpty(existingRepo)) {
// org repo create
await doApiCall(
errors,
Expand All @@ -299,6 +327,23 @@ async function upsertRepo(
)
}

async function createOrgsAndTeams(orgApi: OrganizationApi, existingOrganizations: Organization[], teamIds: string[]) {
await Promise.all(
teamIds.map((organizationName) => {
return upsertOrganization(orgApi, existingOrganizations, organizationName)
}),
).then(() => {
teamIds
.filter((id) => !id.includes('otomi'))
.map((teamId) => {
const name = `team-${teamId}`
return upsertTeam(orgApi, orgName, { ...adminTeam, name })
})
})
// create org wide viewer team for otomi role "team-viewer"
await upsertTeam(orgApi, orgName, readOnlyTeam)
}

async function hasSpecificHook(repoApi: RepositoryApi, hookToFind: string): Promise<hookInfo> {
const hooks: any[] = await doApiCall(
errors,
Expand Down Expand Up @@ -347,24 +392,6 @@ async function addTektonHook(repoApi: RepositoryApi): Promise<void> {
}
}

async function createOrgAndTeams(orgApi: OrganizationApi, existingTeams: Team[], teamIds: string[], teamConfig: any) {
const orgOption = { ...new CreateOrgOption(), username: orgName, repoAdminChangeTeamAccess: true }
await doApiCall(errors, `Creating org "${orgName}"`, () => orgApi.orgCreate(orgOption), 422)

// create all the teams first
await Promise.all(
teamIds.map((teamId) => {
// determine self service flags
const name = `team-${teamId}`
if ((teamConfig[teamId]?.selfService?.apps || []).includes('gitea'))
return upsertTeam(existingTeams, orgApi, { ...adminTeam, name })
return upsertTeam(existingTeams, orgApi, { ...editorTeam, name })
}),
)
// create org wide viewer team for otomi role "team-viewer"
await upsertTeam(existingTeams, orgApi, readOnlyTeam)
}

async function createReposAndAddToTeam(
orgApi: OrganizationApi,
repoApi: RepositoryApi,
Expand All @@ -377,38 +404,39 @@ async function createReposAndAddToTeam(
await upsertRepo(existingRepos, orgApi, repoApi, { ...repoOption, name: otomiChartsRepoName })

// add repo: otomi/values to the team: otomi-viewer
await doApiCall(
errors,
`Adding repo ${otomiValuesRepoName} to team ${teamNameViewer}`,
() => repoApi.repoAddTeam(orgName, otomiValuesRepoName, teamNameViewer),
422,
)

// add repo: otomi/charts to the team: otomi-viewer
await doApiCall(
errors,
`Adding repo ${otomiChartsRepoName} to team ${teamNameViewer}`,
() => repoApi.repoAddTeam(orgName, otomiChartsRepoName, teamNameViewer),
422,
)
const existingValuesRepo = existingRepos.find((repo) => repo.name === otomiValuesRepoName)
const existingChartsRepo = existingRepos.find((repo) => repo.name === otomiChartsRepoName)
if (!existingValuesRepo)
await doApiCall(
errors,
`Adding repo ${otomiValuesRepoName} to team ${teamNameViewer}`,
() => repoApi.repoAddTeam(orgName, otomiValuesRepoName, teamNameViewer),
422,
)
if (!existingChartsRepo)
// add repo: otomi/charts to the team: otomi-viewer
await doApiCall(
errors,
`Adding repo ${otomiChartsRepoName} to team ${teamNameViewer}`,
() => repoApi.repoAddTeam(orgName, otomiChartsRepoName, teamNameViewer),
422,
)
}

async function setupGitea() {
const { giteaPassword, teamConfig, hasArgocd } = env
console.info('Starting Gitea setup/reconfiguration')
const teamIds = Object.keys(teamConfig)
const teamIds = ['otomi', ...Object.keys(teamConfig)].filter((id) => id !== 'admin')
const formattedGiteaUrl: string = GITEA_ENDPOINT.endsWith('/') ? GITEA_ENDPOINT.slice(0, -1) : GITEA_ENDPOINT

// create the org
const orgApi = new OrganizationApi(username, giteaPassword, `${formattedGiteaUrl}/api/v1`)
const repoApi = new RepositoryApi(username, giteaPassword, `${formattedGiteaUrl}/api/v1`)

const existingTeams = await doApiCall(errors, `Getting all teams in org "${orgName}"`, () =>
orgApi.orgListTeams(orgName),
)
await createOrgAndTeams(orgApi, existingTeams, teamIds, teamConfig)
const existingOrganizations = await doApiCall(errors, 'Getting all organizations', () => orgApi.orgGetAll())

await createOrgsAndTeams(orgApi, existingOrganizations, teamIds)

const existingRepos = await doApiCall(errors, `Getting all repos in org "${orgName}"`, () =>
const existingRepos: Repository[] = await doApiCall(errors, `Getting all repos in org "${orgName}"`, () =>
orgApi.orgListRepos(orgName),
)
const repoOption: CreateRepoOption = {
Expand All @@ -426,11 +454,13 @@ async function setupGitea() {

// then create initial gitops repo for teams
await Promise.all(
teamIds.map(async (teamId) => {
const name = `team-${teamId}-argocd`
const option = { ...repoOption, autoInit: true, name }
return upsertRepo(existingRepos, orgApi, repoApi, option, `team-${teamId}`)
}),
teamIds
.filter((id) => !id.includes('otomi'))
.map(async (teamId) => {
const name = `team-${teamId}-argocd`
const option = { ...repoOption, autoInit: true, name }
return upsertRepo(existingRepos, orgApi, repoApi, option, `team-${teamId}`)
}),
)
if (errors.length) {
console.error(`Errors found: ${JSON.stringify(errors, null, 2)}`)
Expand All @@ -445,7 +475,11 @@ export function buildTeamString(teamNames: any[]): string {
const teamObject: groupMapping = { 'platform-admin': { otomi: [teamNameOwners] } }
if (teamNames === undefined) return JSON.stringify(teamObject)
teamNames.forEach((teamName: string) => {
teamObject[`team-${teamName}`] = { otomi: [teamNameViewer, `team-${teamName}`] }
const team = `team-${teamName}`
teamObject[team] = {
otomi: [teamNameViewer, team],
[teamName]: ['owners'],
}
})
return JSON.stringify(teamObject)
}
Expand Down
Loading