diff --git a/scripts/create-secrets.sh b/scripts/create-secrets.sh index 5c8dc21..6644134 100755 --- a/scripts/create-secrets.sh +++ b/scripts/create-secrets.sh @@ -15,6 +15,6 @@ fi kubectl get namespace postgres > /dev/null 2>&1 || kubectl create namespace postgres # Create the secret -kubectl create secret generic cloudflare-api-token-secret \ +kubectl create secret generic cloudflare-api-token \ --namespace cert-manager \ --from-literal=api-token="${CLOUDFLARE_API_KEY}" diff --git a/src/custom-resouces/authentik-server/authentik-server.resource.ts b/src/custom-resouces/authentik-server/authentik-server.resource.ts index 1f5c6a4..c9584ee 100644 --- a/src/custom-resouces/authentik-server/authentik-server.resource.ts +++ b/src/custom-resouces/authentik-server/authentik-server.resource.ts @@ -1,6 +1,5 @@ import type { V1Service, V1Deployment, V1Secret } from '@kubernetes/client-node'; import { z } from 'zod'; -import deepEqual from 'deep-equal'; import { CustomResource, @@ -19,6 +18,7 @@ import { SecretService } from '../../services/secrets/secrets.ts'; import { decodeSecret } from '../../utils/secrets.ts'; import type { postgresDatabaseSecretSchema } from '../postgres-database/postgres-database.resource.ts'; import type { redisConnectionSpecSchema } from '../redis-connection/redis-connection.schemas.ts'; +import { isDeepSubset } from '../../utils/objects.ts'; import { authentikServerSecretSchema, type authentikServerSpecSchema } from './authentik-server.scemas.ts'; import { createDomainService, createManifest, createServiceManifest } from './authentik-server.create-manifests.ts'; @@ -222,7 +222,7 @@ class AuthentikServerResource extends CustomResource kind: 'Certificate', metadata: { name: options.name, - namespace: 'istio-ingress', + namespace: 'homelab', // TODO: use namespace of gateway controller }, spec: { secretName: options.secretName, diff --git a/src/custom-resouces/domain/domain.resource.ts b/src/custom-resouces/domain/domain.resource.ts index 89e9cbc..8b89c13 100644 --- a/src/custom-resouces/domain/domain.resource.ts +++ b/src/custom-resouces/domain/domain.resource.ts @@ -47,7 +47,7 @@ class DomainResource extends CustomResource { apiVersion: 'cert-manager.io/v1', kind: 'Certificate', name: `domain-${this.name}`, - namespace: 'istio-ingress', + namespace: 'homelab', }); this.#gatewayResource.on('changed', this.queueReconcile); @@ -85,7 +85,7 @@ class DomainResource extends CustomResource { namespace: this.name, domain: this.spec.hostname, ref: this.ref, - gateway: istioService.gateway.current.metadata?.labels?.istio || 'ingress', + gateway: istioService.gateway.current.metadata?.labels?.istio || 'gateway-controller', secretName: this.#certSecret, }); if (!deepEqual(this.#gatewayResource.current?.spec, manifest.spec)) { diff --git a/src/custom-resouces/homelab/homelab.manifests.ts b/src/custom-resouces/homelab/homelab.manifests.ts index fe34ad0..b6e73a3 100644 --- a/src/custom-resouces/homelab/homelab.manifests.ts +++ b/src/custom-resouces/homelab/homelab.manifests.ts @@ -257,12 +257,13 @@ const localStorageManifest = (options: LocalStorageManifestOptions): KubernetesO values: { storageClass: { name: 'local-path', + provisionerName: 'rancher.io/local-path', defaultClass: true, }, nodePathMap: [ { node: 'DEFAULT_PATH_FOR_NON_LISTED_NODES', - path: options.storagePath, + paths: [options.storagePath], }, ], helper: { diff --git a/src/custom-resouces/postgres-cluster/postgres-cluster.manifests.ts b/src/custom-resouces/postgres-cluster/postgres-cluster.manifests.ts new file mode 100644 index 0000000..b304d98 --- /dev/null +++ b/src/custom-resouces/postgres-cluster/postgres-cluster.manifests.ts @@ -0,0 +1,124 @@ +import type { V1Deployment, V1PersistentVolumeClaim, V1Service } from '@kubernetes/client-node'; + +import type { CustomResourceObject } from '../../services/custom-resources/custom-resources.custom-resource.ts'; +import type { postgresConnectionSpecSchema } from '../postgres-connection/posgtres-connection.schemas.ts'; +import { API_VERSION } from '../../utils/consts.ts'; + +type PvcOptions = { + name: string; + owner: ExpectedAny; +}; +const pvcManifest = (options: PvcOptions): V1PersistentVolumeClaim => { + return { + apiVersion: 'v1', + kind: 'PersistentVolumeClaim', + metadata: { + ownerReferences: [options.owner], + name: options.name, + labels: { + app: options.name, + }, + annotations: { + 'volume.kubernetes.io/storage-class': 'local-path', + }, + }, + spec: { + accessModes: ['ReadWriteOnce'], + resources: { + requests: { + storage: '10Gi', + }, + }, + }, + }; +}; + +type DeploymentManifetOptions = { + name: string; + owner: ExpectedAny; + user: string; + password: string; +}; +const deploymentManifest = (options: DeploymentManifetOptions): V1Deployment => { + return { + apiVersion: 'apps/v1', + kind: 'Deployment', + metadata: { + ownerReferences: [options.owner], + }, + spec: { + replicas: 1, + selector: { + matchLabels: { + app: options.name, + }, + }, + template: { + metadata: { + labels: { + app: options.name, + }, + }, + spec: { + volumes: [{ name: options.name, persistentVolumeClaim: { claimName: options.name } }], + containers: [ + { + name: options.name, + image: 'postgres:17', + ports: [{ containerPort: 5432 }], + volumeMounts: [{ mountPath: '/var/lib/postgresql/data', name: options.name }], + env: [ + { name: 'POSTGRES_USER', value: options.user }, + { name: 'POSTGRES_PASSWORD', value: options.password }, + ], + }, + ], + }, + }, + }, + }; +}; + +type ServiceManifestOptions = { + name: string; + owner: ExpectedAny; +}; +const serviceManifest = (options: ServiceManifestOptions): V1Service => { + return { + apiVersion: 'v1', + kind: 'Service', + metadata: { + ownerReferences: [options.owner], + name: options.name, + labels: { + app: options.name, + }, + }, + spec: { + type: 'ClusterIP', + ports: [{ port: 5432, targetPort: 5432 }], + selector: { + app: options.name, + }, + }, + }; +}; + +type ConnectionManifestOptions = { + name: string; + owner: ExpectedAny; +}; +const connectionManifest = ( + options: ConnectionManifestOptions, +): CustomResourceObject => ({ + apiVersion: API_VERSION, + kind: 'PostgresConnection', + metadata: { + ownerReferences: [options.owner], + }, + spec: { + secret: `${options.name}-secret`, + }, +}); + +export { pvcManifest, deploymentManifest, serviceManifest, connectionManifest }; diff --git a/src/custom-resouces/postgres-cluster/postgres-cluster.resource.ts b/src/custom-resouces/postgres-cluster/postgres-cluster.resource.ts new file mode 100644 index 0000000..36c7f24 --- /dev/null +++ b/src/custom-resouces/postgres-cluster/postgres-cluster.resource.ts @@ -0,0 +1,170 @@ +import type { V1Deployment, V1PersistentVolumeClaim, V1Service } from '@kubernetes/client-node'; + +import { + CustomResource, + type CustomResourceObject, + type CustomResourceOptions, + type SubresourceResult, +} from '../../services/custom-resources/custom-resources.custom-resource.ts'; +import { ResourceService, type Resource } from '../../services/resources/resources.ts'; +import { + postgresConnectionSecretDataSchema, + type postgresConnectionSpecSchema, +} from '../postgres-connection/posgtres-connection.schemas.ts'; +import { API_VERSION } from '../../utils/consts.ts'; +import { isDeepSubset } from '../../utils/objects.ts'; +import type { EnsuredSecret } from '../../services/secrets/secrets.secret.ts'; +import { SecretService } from '../../services/secrets/secrets.ts'; + +import type { postgresClusterSpecSchema } from './postgres-cluster.schemas.ts'; +import { connectionManifest, deploymentManifest, pvcManifest, serviceManifest } from './postgres-cluster.manifests.ts'; + +class PostgresClusterResource extends CustomResource { + #resources: { + pvc: Resource; + deployment: Resource; + service: Resource; + connection: Resource>; + secret: EnsuredSecret; + }; + + constructor(options: CustomResourceOptions) { + super(options); + const resourceService = this.services.get(ResourceService); + const secretService = this.services.get(SecretService); + this.#resources = { + pvc: resourceService.get({ + apiVersion: 'v1', + kind: 'PersistentVolumeClaim', + name: this.name, + namespace: this.namespace, + }), + deployment: resourceService.get({ + apiVersion: 'apps/v1', + kind: 'Deployment', + name: this.name, + namespace: this.namespace, + }), + service: resourceService.get({ + apiVersion: 'v1', + kind: 'Service', + name: this.name, + namespace: this.namespace, + }), + connection: resourceService.get({ + apiVersion: API_VERSION, + kind: 'PostgresConnection', + name: this.name, + namespace: this.namespace, + }), + secret: secretService.ensure({ + name: `${this.name}-secret`, + namespace: this.namespace, + schema: postgresConnectionSecretDataSchema, + generator: () => ({ + host: `${this.name}.${this.namespace}.svc.cluster.local`, + port: '5432', + user: 'postgres', + password: Buffer.from(crypto.getRandomValues(new Uint8Array(16))).toString('hex'), + }), + }), + }; + } + + #reconcilePvc = async (): Promise => { + const pvc = this.#resources.pvc; + const manifest = pvcManifest({ + name: this.name, + owner: this.ref, + }); + if (!isDeepSubset(pvc.spec, manifest.spec)) { + await pvc.patch(manifest); + return { + ready: false, + syncing: true, + reason: 'UpdatingManifest', + }; + } + return { + ready: true, + }; + }; + + #reconcileDeployment = async (): Promise => { + const secret = this.#resources.secret; + if (!secret.isValid || !secret.value) { + return { + ready: false, + syncing: true, + reason: 'SecretNotReady', + }; + } + const deployment = this.#resources.deployment; + const manifest = deploymentManifest({ + name: this.name, + owner: this.ref, + user: secret.value.user, + password: secret.value.password, + }); + if (!isDeepSubset(deployment.spec, manifest.spec)) { + await deployment.patch(manifest); + return { + ready: false, + syncing: true, + reason: 'UpdatingManifest', + }; + } + return { + ready: true, + }; + }; + + #reconcileService = async (): Promise => { + const service = this.#resources.service; + const manifest = serviceManifest({ + name: this.name, + owner: this.ref, + }); + if (!isDeepSubset(service.spec, manifest.spec)) { + await service.patch(manifest); + return { + ready: false, + syncing: true, + reason: 'UpdatingManifest', + }; + } + return { + ready: true, + }; + }; + + #reconcileConnection = async (): Promise => { + const connection = this.#resources.connection; + const manifest = connectionManifest({ + name: this.name, + owner: this.ref, + }); + if (!isDeepSubset(connection.spec, manifest.spec)) { + await connection.patch(manifest); + return { + ready: false, + syncing: true, + reason: 'UpdatingManifest', + }; + } + return { + ready: true, + }; + }; + + public reconcile = async () => { + await Promise.allSettled([ + this.reconcileSubresource('PVC', this.#reconcilePvc), + this.reconcileSubresource('Deployment', this.#reconcileDeployment), + this.reconcileSubresource('Service', this.#reconcileService), + this.reconcileSubresource('Connection', this.#reconcileConnection), + ]); + }; +} + +export { PostgresClusterResource }; diff --git a/src/custom-resouces/postgres-cluster/postgres-cluster.ts b/src/custom-resouces/postgres-cluster/postgres-cluster.ts new file mode 100644 index 0000000..9703ea5 --- /dev/null +++ b/src/custom-resouces/postgres-cluster/postgres-cluster.ts @@ -0,0 +1,19 @@ +import { createCustomResourceDefinition } from '../../services/custom-resources/custom-resources.ts'; +import { GROUP } from '../../utils/consts.ts'; + +import { postgresClusterSpecSchema } from './postgres-cluster.schemas.ts'; +import { PostgresClusterResource } from './postgres-cluster.resource.ts'; + +const postgresClusterDefinition = createCustomResourceDefinition({ + group: GROUP, + version: 'v1', + kind: 'PostgresCluster', + names: { + plural: 'postgresclusters', + singular: 'postgrescluster', + }, + spec: postgresClusterSpecSchema, + create: (options) => new PostgresClusterResource(options), +}); + +export { postgresClusterDefinition }; diff --git a/src/custom-resouces/postgres-connection/postgres-connection.resource.ts b/src/custom-resouces/postgres-connection/postgres-connection.resource.ts index 8a49d7e..265faed 100644 --- a/src/custom-resouces/postgres-connection/postgres-connection.resource.ts +++ b/src/custom-resouces/postgres-connection/postgres-connection.resource.ts @@ -1,4 +1,5 @@ import type { V1Secret } from '@kubernetes/client-node'; +import type { z } from 'zod'; import { CustomResource, @@ -8,8 +9,12 @@ import { ResourceReference } from '../../services/resources/resources.ref.ts'; import { ResourceService } from '../../services/resources/resources.ts'; import { getWithNamespace } from '../../utils/naming.ts'; import { PostgresService } from '../../services/postgres/postgres.service.ts'; +import { decodeSecret } from '../../utils/secrets.ts'; -import type { postgresConnectionSpecSchema } from './posgtres-connection.schemas.ts'; +import type { + postgresConnectionSecretDataSchema, + postgresConnectionSpecSchema, +} from './posgtres-connection.schemas.ts'; class PostgresConnectionResource extends CustomResource { #secret: ResourceReference; @@ -46,7 +51,9 @@ class PostgresConnectionResource extends CustomResource>( + current.data, + )!; if (!host) { return this.conditions.set('Ready', { status: 'False', diff --git a/src/custom-resouces/postgres-database/postgres-database.resource.ts b/src/custom-resouces/postgres-database/postgres-database.resource.ts index aed5fe4..d741ad9 100644 --- a/src/custom-resouces/postgres-database/postgres-database.resource.ts +++ b/src/custom-resouces/postgres-database/postgres-database.resource.ts @@ -186,10 +186,10 @@ class PostgresDatabaseResource extends CustomResource { - this.#updateSecret(); if (!this.exists || this.metadata.deletionTimestamp) { return; } + this.#updateSecret(); await Promise.allSettled([ await this.reconcileSubresource(DATABASE_READY_CONDITION, this.#reconcileDatabase), await this.reconcileSubresource(SECRET_READY_CONDITION, this.#reconcileSecret), diff --git a/src/custom-resouces/redis-server/redis-server.manifests.ts b/src/custom-resouces/redis-server/redis-server.manifests.ts new file mode 100644 index 0000000..021e215 --- /dev/null +++ b/src/custom-resouces/redis-server/redis-server.manifests.ts @@ -0,0 +1,82 @@ +import type { V1Deployment, V1Service } from '@kubernetes/client-node'; + +import type { CustomResourceObject } from '../../services/custom-resources/custom-resources.custom-resource.ts'; +import type { redisConnectionSpecSchema } from '../redis-connection/redis-connection.schemas.ts'; +import { API_VERSION, CONTROLLED_LABEL } from '../../utils/consts.ts'; + +const deploymentManifest = (): V1Deployment => ({ + apiVersion: 'apps/v1', + kind: 'Deployment', + metadata: { + name: 'redis-server', + namespace: 'homelab', + }, + spec: { + replicas: 1, + selector: { + matchLabels: { + app: 'redis-server', + }, + }, + template: { + metadata: { + labels: { + app: 'redis-server', + }, + }, + spec: { + containers: [ + { + name: 'redis-server', + image: 'redis:latest', + ports: [ + { + containerPort: 6379, + }, + ], + }, + ], + }, + }, + }, +}); + +const serviceManifest = (): V1Service => ({ + apiVersion: 'v1', + kind: 'Service', + metadata: { + name: 'redis-server', + namespace: 'homelab', + }, + spec: { + selector: { + app: 'redis-server', + }, + ports: [ + { + port: 6379, + }, + ], + }, +}); + +type RedisConnectionManifestOptions = { + secretName: string; +}; + +const connectionManifest = ( + options: RedisConnectionManifestOptions, +): CustomResourceObject => ({ + apiVersion: API_VERSION, + kind: 'RedisConnection', + metadata: { + labels: { + ...CONTROLLED_LABEL, + }, + }, + spec: { + secret: options.secretName, + }, +}); + +export { deploymentManifest, serviceManifest, connectionManifest }; diff --git a/src/custom-resouces/redis-server/redis-server.resource.ts b/src/custom-resouces/redis-server/redis-server.resource.ts new file mode 100644 index 0000000..90a927a --- /dev/null +++ b/src/custom-resouces/redis-server/redis-server.resource.ts @@ -0,0 +1,138 @@ +import type { V1Deployment, V1Service } from '@kubernetes/client-node'; + +import { + type CustomResourceOptions, + CustomResource, + type CustomResourceObject, +} from '../../services/custom-resources/custom-resources.custom-resource.ts'; +import { + redisConnectionSecretDataSchema, + redisConnectionSpecSchema, +} from '../redis-connection/redis-connection.schemas.ts'; +import { Resource, ResourceService } from '../../services/resources/resources.ts'; +import { API_VERSION } from '../../utils/consts.ts'; +import type { EnsuredSecret } from '../../services/secrets/secrets.secret.ts'; +import { SecretService } from '../../services/secrets/secrets.ts'; +import { isDeepSubset } from '../../utils/objects.ts'; + +import { redisServerSpecSchema } from './redis-server.schemas.ts'; +import { connectionManifest, deploymentManifest, serviceManifest } from './redis-server.manifests.ts'; + +class RedisServerResource extends CustomResource { + #resources: { + deployment: Resource; + service: Resource; + connection: Resource>; + secret: EnsuredSecret; + }; + constructor(options: CustomResourceOptions) { + super(options); + const resourceService = this.services.get(ResourceService); + const secretService = this.services.get(SecretService); + this.#resources = { + deployment: resourceService.get({ + apiVersion: 'apps/v1', + kind: 'Deployment', + name: this.name, + namespace: this.namespace, + }), + service: resourceService.get({ + apiVersion: 'v1', + kind: 'Service', + name: this.name, + namespace: this.namespace, + }), + connection: resourceService.get({ + apiVersion: API_VERSION, + kind: 'RedisConnection', + name: this.name, + namespace: this.namespace, + }), + secret: secretService.ensure({ + name: `${this.name}-connection`, + namespace: this.namespace, + schema: redisConnectionSecretDataSchema, + generator: () => ({ + host: `${this.name}.${this.namespace}.svc.cluster.local`, + }), + }), + }; + } + + #reconcileDeployment = async () => { + const { deployment } = this.#resources; + const manifest = deploymentManifest(); + if (!isDeepSubset(deployment.spec, manifest.spec)) { + await deployment.patch(manifest); + return { + ready: false, + syncing: true, + reason: 'ChangingDeployment', + message: 'Deployment need changes', + }; + } + return { + ready: true, + reason: 'DeploymentReady', + message: 'Deployment is ready', + }; + }; + + #reconcileService = async () => { + const { service } = this.#resources; + const manifest = serviceManifest(); + if (!isDeepSubset(service.spec, manifest.spec)) { + await service.patch(manifest); + return { + ready: false, + syncing: true, + reason: 'ChangingService', + message: 'Service need changes', + }; + } + return { + ready: true, + reason: 'ServiceReady', + message: 'Service is ready', + }; + }; + + #reconcileConnection = async () => { + const { connection, secret } = this.#resources; + if (!secret.isValid || !secret.value) { + return { + ready: false, + failed: true, + reason: 'MissingSecret', + message: 'Secret is missing', + }; + } + const manifest = connectionManifest({ + secretName: secret.name, + }); + if (!isDeepSubset(connection.spec, manifest.spec)) { + await connection.patch(manifest); + return { + ready: false, + syncing: true, + reason: 'ChangingConnection', + message: 'Connection need changes', + }; + } + return { + ready: true, + reason: 'ConnectionReady', + message: 'Connection is ready', + }; + }; + + public reconcile = async () => { + await Promise.allSettled([ + this.reconcileSubresource('Deployment', this.#reconcileDeployment), + this.reconcileSubresource('Service', this.#reconcileService), + this.reconcileSubresource('Connection', this.#reconcileConnection), + ]); + }; +} + +export { RedisServerResource }; diff --git a/src/custom-resouces/redis-server/redis-server.schemas.ts b/src/custom-resouces/redis-server/redis-server.schemas.ts new file mode 100644 index 0000000..cbd36f2 --- /dev/null +++ b/src/custom-resouces/redis-server/redis-server.schemas.ts @@ -0,0 +1,5 @@ +import { z } from 'zod'; + +const redisServerSpecSchema = z.object({}); + +export { redisServerSpecSchema }; diff --git a/src/custom-resouces/redis-server/redis-server.ts b/src/custom-resouces/redis-server/redis-server.ts new file mode 100644 index 0000000..50879b6 --- /dev/null +++ b/src/custom-resouces/redis-server/redis-server.ts @@ -0,0 +1,19 @@ +import { createCustomResourceDefinition } from '../../services/custom-resources/custom-resources.ts'; +import { GROUP } from '../../utils/consts.ts'; + +import { RedisServerResource } from './redis-server.resource.ts'; +import { redisServerSpecSchema } from './redis-server.schemas.ts'; + +const redisServerDefinition = createCustomResourceDefinition({ + group: GROUP, + version: 'v1', + kind: 'RedisServer', + names: { + plural: 'redis-servers', + singular: 'redis-server', + }, + spec: redisServerSpecSchema, + create: (options) => new RedisServerResource(options), +}); + +export { redisServerDefinition }; diff --git a/src/services/custom-resources/custom-resources.custom-resource.ts b/src/services/custom-resources/custom-resources.custom-resource.ts index 8c79590..a513819 100644 --- a/src/services/custom-resources/custom-resources.custom-resource.ts +++ b/src/services/custom-resources/custom-resources.custom-resource.ts @@ -89,20 +89,18 @@ abstract class CustomResource extends EventEmitter extends EventEmitter extends EventEmitter; diff --git a/src/services/secrets/secrets.secret.ts b/src/services/secrets/secrets.secret.ts index 93db217..5563c00 100644 --- a/src/services/secrets/secrets.secret.ts +++ b/src/services/secrets/secrets.secret.ts @@ -33,6 +33,14 @@ class EnsuredSecret { this.#handleChanged(); } + public get name() { + return this.#options.name; + } + + public get namespace() { + return this.#options.namespace; + } + public get resouce() { return this.#resource; } diff --git a/test-manifests/authentik-server.yaml b/test-manifests/authentik-server.yaml index 28e809f..9561e07 100644 --- a/test-manifests/authentik-server.yaml +++ b/test-manifests/authentik-server.yaml @@ -1,9 +1,10 @@ apiVersion: 'homelab.mortenolsen.pro/v1' kind: 'AuthentikServer' metadata: - name: homelab + name: authentik namespace: homelab spec: domain: homelab - database: test2 + subdomain: authentik + database: postgres redis: redis diff --git a/test-manifests/domain-service.yaml b/test-manifests/domain-service.yaml index e802504..0ea5936 100644 --- a/test-manifests/domain-service.yaml +++ b/test-manifests/domain-service.yaml @@ -7,6 +7,6 @@ spec: domain: homelab/homelab subdomain: test destination: - host: foo.svc.cluster.local + host: authentik.svc.cluster.local port: number: 80 diff --git a/test-manifests/redis-connection.yaml b/test-manifests/postgres-cluster.yaml similarity index 50% rename from test-manifests/redis-connection.yaml rename to test-manifests/postgres-cluster.yaml index ea747ee..1ba7775 100644 --- a/test-manifests/redis-connection.yaml +++ b/test-manifests/postgres-cluster.yaml @@ -1,7 +1,6 @@ apiVersion: 'homelab.mortenolsen.pro/v1' -kind: 'RedisConnection' +kind: 'PostgresCluster' metadata: - name: 'redis' + name: 'postgres' namespace: 'homelab' -spec: - secret: redis/connection +spec: {} diff --git a/test-manifests/postgres-connection.yaml b/test-manifests/postgres-connection.yaml deleted file mode 100644 index c6bd4ea..0000000 --- a/test-manifests/postgres-connection.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: 'homelab.mortenolsen.pro/v1' -kind: 'PostgresConnection' -metadata: - name: 'db' - namespace: 'homelab' -spec: - secret: postgres/postgres-secret diff --git a/test-manifests/postgres-database.yaml b/test-manifests/postgres-database.yaml index 8a96319..05a697d 100644 --- a/test-manifests/postgres-database.yaml +++ b/test-manifests/postgres-database.yaml @@ -1,7 +1,7 @@ apiVersion: 'homelab.mortenolsen.pro/v1' kind: 'PostgresDatabase' metadata: - name: 'test2' + name: postgres namespace: 'homelab' spec: - connection: homelab/db + connection: homelab/postgres diff --git a/test-manifests/redis-database.yaml b/test-manifests/redis-database.yaml new file mode 100644 index 0000000..230933d --- /dev/null +++ b/test-manifests/redis-database.yaml @@ -0,0 +1,6 @@ +apiVersion: 'homelab.mortenolsen.pro/v1' +kind: 'RedisServer' +metadata: + name: redis + namespace: 'homelab' +spec: {}