Skip to content

Commit

Permalink
feat(server): add k8s patch method to k8s service (#430)
Browse files Browse the repository at this point in the history
Signed-off-by: maslow <wangfugen@126.com>
  • Loading branch information
maslow authored Nov 25, 2022
1 parent c0f04e1 commit ad79951
Show file tree
Hide file tree
Showing 13 changed files with 186 additions and 57 deletions.
11 changes: 11 additions & 0 deletions server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@prisma/client": "^4.6.1",
"casdoor-nodejs-sdk": "^1.3.0",
"dotenv": "^16.0.3",
"fast-json-patch": "^3.1.1",
"nanoid": "^3.3.4",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
Expand Down
12 changes: 6 additions & 6 deletions server/src/applications/applications.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ export class ApplicationsService {

async findAll(labelSelector?: string): Promise<ApplicationList> {
const res = await this.k8sClient.customObjectApi.listClusterCustomObject(
Application.Group,
Application.Version,
Application.PluralName,
Application.GVK.group,
Application.GVK.version,
Application.GVK.plural,
undefined,
undefined,
undefined,
Expand All @@ -95,10 +95,10 @@ export class ApplicationsService {
try {
const appRes =
await this.k8sClient.customObjectApi.getNamespacedCustomObject(
Application.Group,
Application.Version,
Application.GVK.group,
Application.GVK.version,
namespace,
Application.PluralName,
Application.GVK.plural,
name,
)
return appRes.body as Application
Expand Down
25 changes: 14 additions & 11 deletions server/src/applications/entities/application.entity.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { KubernetesObject } from '@kubernetes/client-node'
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'
import { ResourceLabels } from 'src/constants'
import { Condition, ObjectMeta } from '../../core/kubernetes.interface'
import { ResourceLabels } from '../../constants'
import {
Condition,
GroupVersionKind,
ObjectMeta,
} from '../../core/kubernetes.interface'
import { BundleSpec } from './bundle.entity'
import { RuntimeSpec } from './runtime.entity'

Expand Down Expand Up @@ -84,17 +88,16 @@ export class Application implements KubernetesObject {
@ApiPropertyOptional()
status?: ApplicationStatus

static readonly Group = 'application.laf.dev'
static readonly Version = 'v1'
static readonly PluralName = 'applications'
static readonly Kind = 'Application'
static get GroupVersion() {
return `${this.Group}/${this.Version}`
}
static readonly GVK = new GroupVersionKind(
'application.laf.dev',
'v1',
'Application',
'applications',
)

constructor(name: string, namespace: string) {
this.apiVersion = Application.GroupVersion
this.kind = Application.Kind
this.apiVersion = Application.GVK.apiVersion
this.kind = Application.GVK.kind
this.metadata = new ObjectMeta(name, namespace)
this.metadata.labels = {}
this.spec = new ApplicationSpec()
Expand Down
2 changes: 1 addition & 1 deletion server/src/applications/entities/bundle.entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { KubernetesObject } from '@kubernetes/client-node'
import { ApiProperty } from '@nestjs/swagger'
import { ObjectMeta } from 'src/core/kubernetes.interface'
import { ObjectMeta } from '../../core/kubernetes.interface'

export class BundleSpec {
@ApiProperty()
Expand Down
2 changes: 1 addition & 1 deletion server/src/applications/entities/runtime.entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { KubernetesObject } from '@kubernetes/client-node'
import { ApiProperty } from '@nestjs/swagger'
import { ObjectMeta } from 'src/core/kubernetes.interface'
import { ObjectMeta } from '../../core/kubernetes.interface'

export class RuntimeVersion {
@ApiProperty()
Expand Down
16 changes: 8 additions & 8 deletions server/src/buckets/buckets.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable, Logger } from '@nestjs/common'
import { GetApplicationNamespaceById } from 'src/common/getter'
import { KubernetesService } from 'src/core/kubernetes.service'
import { GetApplicationNamespaceById } from '../common/getter'
import { KubernetesService } from '../core/kubernetes.service'
import { CreateBucketDto } from './dto/create-bucket.dto'
import { UpdateBucketDto } from './dto/update-bucket.dto'
import { Bucket, BucketList } from './entities/bucket.entity'
Expand Down Expand Up @@ -36,10 +36,10 @@ export class BucketsService {
async findAll(appid: string, labelSelector?: string) {
const namespace = GetApplicationNamespaceById(appid)
const res = await this.k8sClient.customObjectApi.listNamespacedCustomObject(
Bucket.Group,
Bucket.Version,
Bucket.GVK.group,
Bucket.GVK.version,
namespace,
Bucket.PluralName,
Bucket.GVK.plural,
undefined,
undefined,
undefined,
Expand All @@ -54,10 +54,10 @@ export class BucketsService {
try {
const res =
await this.k8sClient.customObjectApi.getNamespacedCustomObject(
Bucket.Group,
Bucket.Version,
Bucket.GVK.group,
Bucket.GVK.version,
namespace,
Bucket.PluralName,
Bucket.GVK.plural,
name,
)
return res.body as Bucket
Expand Down
25 changes: 14 additions & 11 deletions server/src/buckets/entities/bucket.entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { KubernetesObject, V1ObjectMeta } from '@kubernetes/client-node'
import { KubernetesObject } from '@kubernetes/client-node'
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'
import { Condition, ObjectMeta } from '../../core/kubernetes.interface'
import {
Condition,
GroupVersionKind,
ObjectMeta,
} from '../../core/kubernetes.interface'

export enum BucketPolicy {
Private = 'private',
Expand Down Expand Up @@ -68,17 +72,16 @@ export class Bucket implements KubernetesObject {
@ApiPropertyOptional()
status?: BucketStatus

static readonly Group = 'oss.laf.dev'
static readonly Version = 'v1'
static readonly PluralName = 'buckets'
static readonly Kind = 'Bucket'
static get GroupVersion() {
return `${this.Group}/${this.Version}`
}
static readonly GVK = new GroupVersionKind(
'oss.laf.dev',
'v1',
'Bucket',
'buckets',
)

constructor(name: string, namespace: string) {
this.apiVersion = Bucket.GroupVersion
this.kind = Bucket.Kind
this.apiVersion = Bucket.GVK.apiVersion
this.kind = Bucket.GVK.kind
this.metadata = new ObjectMeta(name, namespace)
this.spec = new BucketSpec()
}
Expand Down
42 changes: 41 additions & 1 deletion server/src/core/kubernetes.interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { V1ObjectMeta } from '@kubernetes/client-node'
import { KubernetesObject, V1ObjectMeta } from '@kubernetes/client-node'
import { ApiProperty } from '@nestjs/swagger'
import * as assert from 'node:assert'

export enum ConditionStatus {
ConditionTrue = 'True',
Expand Down Expand Up @@ -52,3 +53,42 @@ export class Condition {
@ApiProperty()
message?: string
}

export class GroupVersionKind {
group: string

version: string

kind: string

plural: string

constructor(group: string, version: string, kind: string, plural?: string) {
assert(group, 'group is required')
assert(version, 'version is required')
assert(kind, 'kind is required')

this.group = group
this.version = version
this.kind = kind
this.plural = plural
if (!plural) {
this.plural = kind.toLowerCase() + 's'
}
}

static fromKubernetesObject(obj: KubernetesObject): GroupVersionKind {
assert(obj.apiVersion, 'apiVersion is required')
assert(obj.kind, 'kind is required')

return new GroupVersionKind(
obj.apiVersion.split('/')[0],
obj.apiVersion.split('/')[1],
obj.kind,
)
}

get apiVersion(): string {
return `${this.group}/${this.version}`
}
}
29 changes: 29 additions & 0 deletions server/src/core/kubernetes.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { Test, TestingModule } from '@nestjs/testing'
import {
Application,
ApplicationState,
} from '../applications/entities/application.entity'
import { ResourceLabels } from '../constants'
import { KubernetesService } from './kubernetes.service'

Expand Down Expand Up @@ -86,3 +90,28 @@ describe.skip('list custom objects with label', () => {
console.log(res.body)
})
})

describe.skip('patch custom objects', () => {
it('should be able to patch custom objects', async () => {
const name = '1i43zq'
const namespace = name
const res = await service.customObjectApi.getNamespacedCustomObject(
Application.GVK.group,
Application.GVK.version,
namespace,
Application.GVK.plural,
name,
)

const data = res.body as Application
data.spec = {
...data.spec,
state: ApplicationState.ApplicationStateRunning,
}

const res2 = await service.patchCustomObject(data).catch((err) => {
console.log(err)
})
console.log('patched', res2)
})
})
43 changes: 43 additions & 0 deletions server/src/core/kubernetes.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Injectable } from '@nestjs/common'
import * as k8s from '@kubernetes/client-node'
import { KubernetesObject } from '@kubernetes/client-node'
import { compare } from 'fast-json-patch'
import { GroupVersionKind } from './kubernetes.interface'
import path from 'path'

/**
* Single instance of the Kubernetes API client.
Expand Down Expand Up @@ -113,4 +117,43 @@ export class KubernetesService {
}
return deleted
}

async patchCustomObject(spec: KubernetesObject) {
const client = this.customObjectApi
const gvk = GroupVersionKind.fromKubernetesObject(spec)

// get the current spec
const res = await client.getNamespacedCustomObject(
gvk.group,
gvk.version,
spec.metadata.namespace,
gvk.plural,
spec.metadata.name,
)
const currentSpec = res.body as KubernetesObject

// calculate the patch
const patch = compare(currentSpec, spec)
const options = {
headers: {
'Content-Type': k8s.PatchUtils.PATCH_FORMAT_JSON_PATCH,
},
}

// apply the patch
const response = await client.patchNamespacedCustomObject(
gvk.group,
gvk.version,
spec.metadata.namespace,
gvk.plural,
spec.metadata.name,
patch,
undefined,
undefined,
undefined,
options,
)

return response.body
}
}
19 changes: 9 additions & 10 deletions server/src/functions/entities/function.entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { KubernetesListObject, KubernetesObject } from '@kubernetes/client-node'
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'
import { ObjectMeta } from 'src/core/kubernetes.interface'
import { GroupVersionKind, ObjectMeta } from '../../core/kubernetes.interface'

export class CloudFunctionSource {
@ApiProperty()
Expand Down Expand Up @@ -62,17 +62,16 @@ export class CloudFunction implements KubernetesObject {
@ApiPropertyOptional()
status?: CloudFunctionStatus

static readonly Group = 'runtime.laf.dev'
static readonly Version = 'v1'
static readonly PluralName = 'functions'
static readonly Kind = 'Function'
static get GroupVersion() {
return `${this.Group}/${this.Version}`
}
static readonly GVK = new GroupVersionKind(
'runtime.laf.dev',
'v1',
'Function',
'functions',
)

constructor(name: string, namespace: string) {
this.apiVersion = CloudFunction.GroupVersion
this.kind = CloudFunction.Kind
this.apiVersion = CloudFunction.GVK.apiVersion
this.kind = CloudFunction.GVK.kind
this.metadata = {
name,
namespace,
Expand Down
Loading

0 comments on commit ad79951

Please sign in to comment.