Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export default defineConfig([
varsIgnorePattern: '^_',
},
],

'@typescript-eslint/no-use-before-define': ['error'],
'@typescript-eslint/restrict-template-expressions': 'off',
'@typescript-eslint/naming-convention': [
Expand Down
7 changes: 7 additions & 0 deletions src/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,13 @@ export class Git {
await writeFile(absolutePath, content, 'utf8')
}

async writeTextFile(file: string, content: string): Promise<void> {
const absolutePath = join(this.path, file)
debug(`Writing to file: ${absolutePath}`)
await ensureDir(dirname(absolutePath))
await writeFile(absolutePath, content, 'utf8')
}

async fileExists(relativePath: string): Promise<boolean> {
const absolutePath = join(this.path, relativePath)
return await pathExists(absolutePath)
Expand Down
1 change: 1 addition & 0 deletions src/otomi-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ export interface Repo {
users: User[]
versions: Versions
teamConfig: Record<string, TeamConfig>
files: Record<string, string>
}

export interface TeamConfig {
Expand Down
54 changes: 30 additions & 24 deletions src/otomi-stack.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CoreV1Api, KubeConfig, User as k8sUser, V1ObjectReference } from '@kubernetes/client-node'
import { CoreV1Api, User as k8sUser, KubeConfig, V1ObjectReference } from '@kubernetes/client-node'
import Debug from 'debug'

import { getRegions, ObjectStorageKeyRegions } from '@linode/api-v4'
Expand Down Expand Up @@ -94,6 +94,10 @@ import {
} from 'src/validators'
import { v4 as uuidv4 } from 'uuid'
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml'
import { getAIModels } from './ai/aiModelHandler'
import { AkamaiAgentCR } from './ai/AkamaiAgentCR'
import { AkamaiKnowledgeBaseCR } from './ai/AkamaiKnowledgeBaseCR'
import { DatabaseCR } from './ai/DatabaseCR'
import {
apply,
checkPodExists,
Expand Down Expand Up @@ -121,10 +125,6 @@ import { getSealedSecretsPEM, sealedSecretManifest, SealedSecretManifestType } f
import { getKeycloakUsers, isValidUsername } from './utils/userUtils'
import { ObjectStorageClient } from './utils/wizardUtils'
import { fetchChartYaml, fetchWorkloadCatalog, NewHelmChartValues, sparseCloneChart } from './utils/workloadUtils'
import { getAIModels } from './ai/aiModelHandler'
import { AkamaiKnowledgeBaseCR } from './ai/AkamaiKnowledgeBaseCR'
import { AkamaiAgentCR } from './ai/AkamaiAgentCR'
import { DatabaseCR } from './ai/DatabaseCR'

interface ExcludedApp extends App {
managed: boolean
Expand Down Expand Up @@ -159,6 +159,14 @@ function getTeamSealedSecretsValuesFilePath(teamId: string, sealedSecretsName: s
return `env/teams/${teamId}/sealedsecrets/${sealedSecretsName}`
}

function getTeamWorkloadValuesManagedFilePath(teamId: string, workloadName: string): string {
return `env/teams/${teamId}/workloadValues/${workloadName}.managed.yaml`
}

function getTeamWorkloadValuesFilePath(teamId: string, workloadName: string): string {
return `env/teams/${teamId}/workloadValues/${workloadName}.yaml`
}

function getTeamKnowledgeBaseValuesFilePath(teamId: string, knowledgeBaseName: string): string {
return `env/teams/${teamId}/knowledgebases/${knowledgeBaseName}`
}
Expand All @@ -184,6 +192,10 @@ export default class OtomiStack {
this.sessionId = sessionId ?? 'main'
}

getFileContent(path: string): string {
return this.repoService.getRepo().files[path] || ''
}

getAppList() {
let apps = getAppList()
apps = apps.filter((item) => item !== 'ingress-nginx')
Expand Down Expand Up @@ -272,16 +284,13 @@ export default class OtomiStack {
})
}

transformWorkloads(workloads: AplWorkloadResponse[], workloadValues: Record<string, any>[]): AplWorkloadResponse[] {
const values = {}
workloadValues.forEach((value) => {
const workloadName = value.name
if (workloadName) {
values[workloadName] = value.values
}
})
transformWorkloads(workloads: AplWorkloadResponse[], files: string[]): AplWorkloadResponse[] {
return workloads.map((workload) => {
return merge(workload, { spec: { values: values[workload.metadata.name] } })
const workloadName = workload.metadata.name
const teamId = workload.metadata.labels?.['apl.io/teamId']

const filePath = getTeamWorkloadValuesFilePath(teamId, workloadName)
return merge(workload, { spec: { values: files[filePath] } })
})
}

Expand Down Expand Up @@ -314,10 +323,9 @@ export default class OtomiStack {
apps: this.transformApps(teamConfig.apps),
policies: this.transformPolicies(teamName, teamConfig.policies || {}),
sealedsecrets: this.transformSecrets(teamName, teamConfig.sealedsecrets || []),
workloads: this.transformWorkloads(teamConfig.workloads || [], teamConfig.workloadValues || []),
workloads: this.transformWorkloads(teamConfig.workloads || [], rawRepo.files || {}),
settings: this.transformTeamSettings(teamConfig.settings),
}))

const repo = rawRepo as Repo
this.repoService = new RepoService(repo)
}
Expand Down Expand Up @@ -839,14 +847,10 @@ export default class OtomiStack {
const { metadata } = data
const teamId = metadata.labels['apl.io/teamId']!
debug(`Saving AplTeamWorkloadValues ${metadata.name} for team ${teamId}`)
const values = {
name: metadata.name,
values: data.spec.values,
}
const configKey = this.getConfigKey('AplTeamWorkloadValues')
const repo = this.createTeamConfigInRepo(teamId, configKey, [values])
const fileMap = getFileMaps('').find((fm) => fm.kind === 'AplTeamWorkloadValues')!
await this.git.saveConfig(repo, fileMap, false)
const filePath = getTeamWorkloadValuesFilePath(teamId, metadata.name)
await this.git.writeTextFile(filePath, data.spec.values || '{}')
const filePathValuesManaged = getTeamWorkloadValuesManagedFilePath(teamId, metadata.name)
await this.git.writeTextFile(filePathValuesManaged, '')
}

async saveTeamPolicy(teamId: string, data: AplPolicyResponse): Promise<void> {
Expand Down Expand Up @@ -885,8 +889,10 @@ export default class OtomiStack {
const fileMapWorkload = getFileMaps('').find((fm) => fm.kind === 'AplTeamWorkload')!
const repoValues = this.createTeamConfigInRepo(teamId, 'workloads', [data])
const fileMapValues = getFileMaps('').find((fm) => fm.kind === 'AplTeamWorkloadValues')!
const filePathValuesManaged = getTeamWorkloadValuesManagedFilePath(teamId, metadata.name)
await this.git.deleteConfig(repoWorkload, fileMapWorkload)
await this.git.deleteConfig(repoValues, fileMapValues)
await this.git.removeFile(filePathValuesManaged)
}

getTeamBackups(teamId: string): Backup[] {
Expand Down
12 changes: 7 additions & 5 deletions src/repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,10 +419,6 @@ export function getFileMap(kind: AplKind, envDir: string): FileMap {
}

export function renderManifest(fileMap: FileMap, jsonPath: jsonpath.PathComponent[], data: Record<string, any>) {
//TODO remove this custom workaround for workloadValues
if (fileMap.kind === 'AplTeamWorkloadValues') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we completely remove the AplTeamWorkloadValues kind, or do we still need it?

return { values: data.values }
}
let spec = data
const labels = {}
if (fileMap.resourceGroup === 'team') {
Expand Down Expand Up @@ -489,6 +485,10 @@ export async function loadFileToSpec(
const jsonPath = getJsonPath(fileMap, filePath)
try {
const data = (await deps.loadYaml(filePath)) || {}
// ensure that local path does not include envDir and the leading slash
const localFilePath = filePath.replace(fileMap.envDir, '').replace(/^\/+/, '')
// eslint-disable-next-line no-param-reassign
spec.files[localFilePath] = data
if (fileMap.processAs === 'arrayItem') {
const ref: Record<string, any>[] = get(spec, jsonPath)
const name = filePath.match(/\/([^/]+)\.yaml$/)?.[1]
Expand Down Expand Up @@ -577,7 +577,9 @@ export async function loadToSpec(
export async function loadValues(envDir: string, deps = { loadToSpec }): Promise<Record<string, any>> {
//We need everything to load to spec for the API
const fileMaps = getFileMaps(envDir)
const spec = {}
const spec = {
files: {},
}

await Promise.all(
fileMaps.map(async (fileMap) => {
Expand Down
1 change: 1 addition & 0 deletions src/services/RepoService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('RepoService', () => {
obj: {},
oidc: { issuer: 'https://issuer.com', clientID: 'client-id', clientSecret: 'client-secret' },
versions: { version: '1.0.0' },
files: {},
} as Repo
service = new RepoService(repo)
service.createTeamConfig(teamSettings)
Expand Down
1 change: 1 addition & 0 deletions src/services/RepoService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class RepoService {
this.repo.users ??= []
this.repo.versions ??= {} as Versions
this.repo.teamConfig ??= {}
this.repo.files ??= {}
}

public getTeamConfigService(teamName: string): TeamConfigService {
Expand Down
Loading