add authentik connection crd

This commit is contained in:
Morten Olsen
2025-08-11 23:16:12 +02:00
parent 130bfec468
commit d4b56007f1
9 changed files with 289 additions and 6 deletions

View File

@@ -70,7 +70,7 @@ class AuthentikClientResource extends CustomResource<typeof authentikClientSpecS
message: 'Server secret not found',
};
}
const url = serverSecretData.data.external_url;
const url = serverSecretData.data.url;
const appName = this.name;
const clientSecretData = authentikClientSecretSchema.safeParse(decodeSecret(this.#clientSecretResource.data));
@@ -139,8 +139,8 @@ class AuthentikClientResource extends CustomResource<typeof authentikClientSpecS
const authentikService = this.services.get(AuthentikService);
const authentikServer = authentikService.get({
url: {
internal: serverSecretData.data.internal_url,
external: serverSecretData.data.external_url,
internal: `${serverSecretData.data.name}.${serverSecret.namespace}.svc.cluster.local`,
external: serverSecretData.data.url,
},
token: serverSecretData.data.token,
});

View File

@@ -14,8 +14,8 @@ const authentikClientSpecSchema = z.object({
});
const authentikClientServerSecretSchema = z.object({
internal_url: z.string(),
external_url: z.string(),
name: z.string(),
url: z.string(),
token: z.string(),
});

View File

@@ -0,0 +1,93 @@
import type { V1Secret } from '@kubernetes/client-node';
import deepEqual from 'deep-equal';
import {
CustomResource,
type CustomResourceOptions,
} from '../../services/custom-resources/custom-resources.custom-resource.ts';
import { ResourceService, type Resource } from '../../services/resources/resources.ts';
import type { ValueReference } from '../../services/value-reference/value-reference.instance.ts';
import { ValueReferenceService } from '../../services/value-reference/value-reference.ts';
import { decodeSecret, encodeSecret } from '../../utils/secrets.ts';
import type { authentikConnectionSpecSchema } from './authentik-connection.schemas.ts';
class AuthentikConnectionResource extends CustomResource<typeof authentikConnectionSpecSchema> {
#name: ValueReference;
#url: ValueReference;
#token: ValueReference;
#secret: Resource<V1Secret>;
constructor(options: CustomResourceOptions<typeof authentikConnectionSpecSchema>) {
super(options);
const valueReferenceService = this.services.get(ValueReferenceService);
const resourceService = this.services.get(ResourceService);
this.#name = valueReferenceService.get(this.namespace);
this.#url = valueReferenceService.get(this.namespace);
this.#token = valueReferenceService.get(this.namespace);
this.#secret = resourceService.get({
apiVersion: 'v1',
kind: 'Secret',
name: `${this.name}-authentik-server`,
namespace: this.namespace,
});
this.#name.on('changed', this.queueReconcile);
this.#url.on('changed', this.queueReconcile);
this.#token.on('changed', this.queueReconcile);
this.#secret.on('changed', this.queueReconcile);
}
#updateResources = () => {
this.#name.ref = this.spec.name;
this.#url.ref = this.spec.url;
this.#token.ref = this.spec.token;
};
public reconcile = async () => {
this.#updateResources();
const name = this.#name.value;
const url = this.#url.value;
const token = this.#token.value;
if (!name) {
return await this.conditions.set('Ready', {
status: 'False',
reason: 'MissingName',
});
}
if (!url) {
return await this.conditions.set('Ready', {
status: 'False',
reason: 'MissingUrl',
});
}
if (!token) {
return await this.conditions.set('Ready', {
status: 'False',
reason: 'MissingToken',
});
}
const values = {
name,
url,
token,
};
const secretValue = decodeSecret(this.#secret.data);
if (!deepEqual(secretValue, values)) {
await this.#secret.patch({
data: encodeSecret(values),
});
return await this.conditions.set('Ready', {
status: 'False',
reason: 'UpdatingSecret',
});
}
return await this.conditions.set('Ready', {
status: 'True',
});
};
}
export { AuthentikConnectionResource };

View File

@@ -0,0 +1,11 @@
import { z } from 'zod';
import { valueReferenceInfoSchema } from '../../services/value-reference/value-reference.instance.ts';
const authentikConnectionSpecSchema = z.object({
name: valueReferenceInfoSchema,
url: valueReferenceInfoSchema,
token: valueReferenceInfoSchema,
});
export { authentikConnectionSpecSchema };

View File

@@ -0,0 +1,19 @@
import { createCustomResourceDefinition } from '../../services/custom-resources/custom-resources.ts';
import { GROUP } from '../../utils/consts.ts';
import { AuthentikConnectionResource } from './authentik-connection.resource.ts';
import { authentikConnectionSpecSchema } from './authentik-connection.schemas.ts';
const authentikConnectionDefinition = createCustomResourceDefinition({
group: GROUP,
version: 'v1',
kind: 'AuthentikConnection',
names: {
plural: 'authentikconnections',
singular: 'authentikconnection',
},
spec: authentikConnectionSpecSchema,
create: (options) => new AuthentikConnectionResource(options),
});
export { authentikConnectionDefinition };

View File

@@ -1,7 +1,13 @@
import { authentikClientDefinition } from './authentik-client/authentik-client.ts';
import { authentikConnectionDefinition } from './authentik-connection/authentik-connection.ts';
import { generateSecretDefinition } from './generate-secret/generate-secret.ts';
import { postgresDatabaseDefinition } from './postgres-database/postgres-database.ts';
const customResources = [postgresDatabaseDefinition, authentikClientDefinition, generateSecretDefinition];
const customResources = [
postgresDatabaseDefinition,
authentikClientDefinition,
generateSecretDefinition,
authentikConnectionDefinition,
];
export { customResources };