Skip to content

Commit ca9eed1

Browse files
CasLubberssvcAPLBotferruhcihan
authored
feat: make api work with external Git source (#915)
* feat: make api work with external Git source * feat: only get internalRepoUrls if Gitea is enabled * fix: teamApps setting enabled correctly * fix: rename gitea references to git * feat: add git repoUrl to settingsinfo.yaml * feat: update src/otomi-stack.ts Co-authored-by: Ferruh <63190600+ferruhcihan@users.noreply.github.com> --------- Co-authored-by: svcAPLBot <174728082+svcAPLBot@users.noreply.github.com> Co-authored-by: Ferruh <63190600+ferruhcihan@users.noreply.github.com>
1 parent 1fe6405 commit ca9eed1

8 files changed

Lines changed: 101 additions & 47 deletions

File tree

docs/gitops.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ sequenceDiagram
116116

117117
## Version two
118118

119-
The locking mechanism is removed by removing the master session controller concept. The session repo controller pulls and pushes from/to Gitea instead of master repo. The Session Controller is also renamed to `Git handler` to not confuse it with user session.
119+
The locking mechanism is removed by removing the master session controller concept. The session repo controller pulls and pushes from/to Git instead of master repo. The Session Controller is also renamed to `Git handler` to not confuse it with user session.
120120

121121
**Sequence diagram for the accepted request**
122122
The following diagram presents GitOps without locking mechanism. It is worth noting that is performs eight operations less comparing to its predecessor.

src/app.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,15 @@ import {
2727
CHECK_LATEST_COMMIT_INTERVAL,
2828
cleanEnv,
2929
EXPRESS_PAYLOAD_LIMIT,
30-
GIT_PASSWORD,
3130
GIT_PUSH_RETRIES,
32-
GIT_USER,
3331
TRUST_PROXY,
3432
} from 'src/validators'
3533
import swaggerUi from 'swagger-ui-express'
36-
import giteaCheckLatest from './gitea/connect'
34+
import getLatestRemoteCommitSha from './git/connect'
3735
import { getBuildStatus, getSealedSecretStatus, getServiceStatus, getWorkloadStatus } from './k8s_operations'
3836

3937
const env = cleanEnv({
4038
CHECK_LATEST_COMMIT_INTERVAL,
41-
GIT_USER,
42-
GIT_PASSWORD,
4339
EXPRESS_PAYLOAD_LIMIT,
4440
GIT_PUSH_RETRIES,
4541
TRUST_PROXY,
@@ -54,14 +50,13 @@ type OtomiSpec = {
5450
valuesSchema: Record<string, any>
5551
}
5652

57-
// get the latest commit from Gitea and checks it against the local values
58-
const checkAgainstGitea = async () => {
59-
const encodedToken = Buffer.from(`${env.GIT_USER}:${env.GIT_PASSWORD}`).toString('base64')
53+
// get the latest commit from Git and checks it against the local values
54+
const checkAgainstGit = async () => {
6055
const otomiStack = await getSessionStack()
61-
const latestOtomiVersion = await giteaCheckLatest(encodedToken)
56+
const latestOtomiVersion = await getLatestRemoteCommitSha()
6257
// check the local version against the latest online version
6358
// if the latest online is newer it will be pulled locally
64-
if (latestOtomiVersion && latestOtomiVersion.data[0].sha !== otomiStack.git.commitSha) {
59+
if (latestOtomiVersion && latestOtomiVersion !== otomiStack.git.commitSha) {
6560
debug('Local values differentiate from Git repository, retrieving latest values')
6661
// Remove all .dec files
6762
await otomiStack.git.git.clean([CleanOptions.FORCE, CleanOptions.IGNORED_ONLY, CleanOptions.RECURSIVE])
@@ -207,7 +202,7 @@ export async function initApp(inOtomiStack?: OtomiStack) {
207202
if (!env.isTest) {
208203
const gitCheckVersionInterval = env.CHECK_LATEST_COMMIT_INTERVAL * 60 * 1000
209204
setInterval(async function () {
210-
await checkAgainstGitea()
205+
await checkAgainstGit()
211206
}, gitCheckVersionInterval)
212207
}
213208
let server: Server | undefined

src/git/connect.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Debug from 'debug'
2+
import simpleGit from 'simple-git'
3+
import { cleanEnv, GIT_BRANCH, GIT_PASSWORD, GIT_REPO_URL, GIT_USER } from 'src/validators'
4+
5+
const debug = Debug('otomi:git-connect')
6+
7+
const env = cleanEnv({
8+
GIT_REPO_URL,
9+
GIT_BRANCH,
10+
GIT_USER,
11+
GIT_PASSWORD,
12+
})
13+
14+
export default async function getLatestRemoteCommitSha(): Promise<string | undefined> {
15+
try {
16+
const git = simpleGit()
17+
const repoUrl = new URL(env.GIT_REPO_URL)
18+
repoUrl.username = encodeURIComponent(env.GIT_USER)
19+
repoUrl.password = encodeURIComponent(env.GIT_PASSWORD)
20+
const result = await git.listRemote(['--refs', repoUrl.toString(), env.GIT_BRANCH])
21+
const [sha] = result.trim().split(/\s/)
22+
if (!sha) {
23+
debug('No remote commit found for branch %s', env.GIT_BRANCH)
24+
return undefined
25+
}
26+
return sha
27+
} catch (error: any) {
28+
debug('Git remote error: ', error.message)
29+
return undefined
30+
}
31+
}

src/gitea/connect.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/openapi/settings.yaml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,42 @@ Settings:
228228
type: object
229229
additionalProperties: false
230230
properties:
231+
git:
232+
type: object
233+
title: Git Configuration
234+
description: |
235+
Git configuration for APL values repository.
236+
additionalProperties: false
237+
properties:
238+
repoUrl:
239+
type: string
240+
description: |
241+
The base URL of the Git repository (without credentials).
242+
pattern: '^https?://.+'
243+
username:
244+
type: string
245+
description: |
246+
Username for authenticating with the Git repository.
247+
Defaults to 'otomi-admin' for internal Gitea.
248+
password:
249+
type: string
250+
description: Password or token for authenticating with the Git repository
251+
x-secret: '{{ randAlphaNum 20 }}'
252+
email:
253+
type: string
254+
description: |
255+
Email address to use for Git commits.
256+
Defaults to 'pipeline@cluster.local' for internal Gitea.
257+
format: email
258+
branch:
259+
type: string
260+
description: The branch to use in the Git repository
261+
required:
262+
- repoUrl
263+
- username
264+
- password
265+
- email
266+
- branch
231267
adminPassword:
232268
description: Master admin password that will be used for all apps that are not configured to use their own password.
233269
$ref: 'definitions.yaml#/adminPassword'

src/openapi/settingsinfo.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ SettingsInfo:
5050
hasExternalIDP:
5151
type: boolean
5252
default: false
53+
git:
54+
properties:
55+
repoUrl:
56+
type: string
57+
description: The base URL of the Git repository (without credentials).
58+
pattern: '^https?://.+'
59+
branch:
60+
type: string
61+
description: The branch to use in the Git repository.
5362
smtp:
5463
properties:
5564
smarthost:

src/otomi-stack.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CoreV1Api, User as k8sUser, KubeConfig, V1ObjectReference } from '@kubernetes/client-node'
1+
import { CoreV1Api, KubeConfig, User as k8sUser, V1ObjectReference } from '@kubernetes/client-node'
22
import Debug from 'debug'
33

44
import { getRegions, ObjectStorageKeyRegions } from '@linode/api-v4'
@@ -295,10 +295,21 @@ export default class OtomiStack {
295295

296296
getSettingsInfo(): SettingsInfo {
297297
const settings = this.getSettings(['cluster', 'dns', 'otomi', 'smtp', 'ingress'])
298+
const otomiInfo = pick(settings.otomi, [
299+
'hasExternalDNS',
300+
'hasExternalIDP',
301+
'isPreInstalled',
302+
'aiEnabled',
303+
'git.repoUrl',
304+
'git.branch',
305+
])
306+
if (otomiInfo.git?.repoUrl?.includes('gitea-http.gitea.svc.cluster.local')) {
307+
otomiInfo.git.repoUrl = `https://gitea.${settings.cluster?.domainSuffix}/otomi/values`
308+
}
298309
return {
299310
cluster: pick(settings.cluster, ['name', 'domainSuffix', 'apiServer', 'provider', 'linode']),
300311
dns: pick(settings.dns, ['zones']),
301-
otomi: pick(settings.otomi, ['hasExternalDNS', 'hasExternalIDP', 'isPreInstalled', 'aiEnabled']),
312+
otomi: otomiInfo,
302313
smtp: pick(settings.smtp, ['smarthost']),
303314
ingressClassNames: map(settings.ingress?.classes, 'className') ?? [],
304315
} as SettingsInfo
@@ -1292,10 +1303,11 @@ export default class OtomiStack {
12921303

12931304
async getInternalRepoUrls(teamId: string): Promise<string[]> {
12941305
if (env.isDev || !teamId || teamId === 'admin') return []
1295-
const { cluster, otomi } = this.getSettings(['cluster', 'otomi'])
12961306
const gitea = this.getApp('gitea')
1297-
const username = (gitea?.values?.adminUsername ?? '') as string
1298-
const password = (gitea?.values?.adminPassword ?? otomi?.adminPassword ?? '') as string
1307+
if (!gitea?.values?.enabled) return []
1308+
const { cluster, otomi } = this.getSettings(['cluster', 'otomi'])
1309+
const username = (otomi?.git?.username ?? '') as string
1310+
const password = (otomi?.git?.password ?? '') as string
12991311
const orgName = `team-${teamId}`
13001312
const domainSuffix = cluster?.domainSuffix
13011313
const internalRepoUrls = (await getGiteaRepoUrls(username, password, orgName, domainSuffix)) || []

src/validators.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const EDITOR_INACTIVITY_TIMEOUT = num({
2828
})
2929
export const GIT_BRANCH = str({ desc: 'The git repo branch', default: 'main' })
3030
export const CHECK_LATEST_COMMIT_INTERVAL = num({
31-
desc: 'Interval in minutes for how much time in between each gitea latest commit check',
31+
desc: 'Interval in minutes for how much time in between each git latest commit check',
3232
default: 2,
3333
})
3434
export const GIT_EMAIL = str({ desc: 'The git user email', default: 'not@us.ed' })

0 commit comments

Comments
 (0)