import type { k8s } from "@highstate/library" import { getOrCreate } from "@highstate/contract" import { toPromise } from "@highstate/pulumi" import { core, type types } from "@pulumi/kubernetes" import { type ComponentResourceOptions, type Input, type Inputs, interpolate, type Output, output, } from "@pulumi/pulumi" import { Namespace } from "./namespace" import { getProvider, mapMetadata, ScopedResource, type ScopedResourceArgs } from "./shared" export type ConfigMapArgs = ScopedResourceArgs & Omit export type CreateOrGetConfigMapArgs = ConfigMapArgs & { /** * The config map entity to patch/retrieve. */ existing: Input | undefined } /** * Represents a Kubernetes ConfigMap resource with metadata and data. */ export abstract class ConfigMap extends ScopedResource { protected constructor( type: string, name: string, args: Inputs, opts: ComponentResourceOptions | undefined, apiVersion: Output, kind: Output, namespace: Output, metadata: Output, /** * The data of the underlying Kubernetes config map. */ readonly data: Output>, ) { super(type, name, args, opts, apiVersion, kind, namespace, metadata) } /** * The Highstate config map entity. */ get entity(): Output { return output({ type: "config-map", clusterId: this.cluster.id, clusterName: this.cluster.name, metadata: this.metadata, }) } /** * Creates a new config map. */ static create(name: string, args: ConfigMapArgs, opts?: ComponentResourceOptions): ConfigMap { return new CreatedConfigMap(name, args, opts) } /** * Creates a new config map or patches an existing one. * * @param name The name of the resource. May not be the same as the config map name. * @param args The arguments to create or patch the config map with. * @param opts Optional resource options. */ static createOrPatch( name: string, args: CreateOrGetConfigMapArgs, opts?: ComponentResourceOptions, ): ConfigMap { if (args.existing) { return new ConfigMapPatch(name, { ...args, name: output(args.existing).metadata.name, }) } return new CreatedConfigMap(name, args, opts) } /** * Creates a new config map or gets an existing one. * * @param name The name of the resource. May not be the same as the config map name. Will not be used when existing config map is retrieved. * @param args The arguments to create or get the config map with. * @param opts Optional resource options. */ static async createOrGet( name: string, args: CreateOrGetConfigMapArgs, opts?: ComponentResourceOptions, ): Promise { if (args.existing) { return await ConfigMap.forAsync(args.existing, output(args.namespace).cluster) } return new CreatedConfigMap(name, args, opts) } /** * Patches an existing config map. * * Will throw an error if the config map does not exist. * * @param name The name of the resource. May not be the same as the config map name. * @param args The arguments to patch the config map with. * @param opts Optional resource options. */ static patch(name: string, args: ConfigMapArgs, opts?: ComponentResourceOptions): ConfigMap { return new ConfigMapPatch(name, args, opts) } /** * Wraps an existing Kubernetes config map. */ static wrap( name: string, args: WrappedConfigMapArgs, opts?: ComponentResourceOptions, ): ConfigMap { return new WrappedConfigMap(name, args, opts) } /** * Gets an existing config map. * * Will throw an error if the config map does not exist. */ static get( name: string, args: ExternalConfigMapArgs, opts?: ComponentResourceOptions, ): ConfigMap { return new ExternalConfigMap(name, args, opts) } private static readonly configMapCache = new Map() /** * Gets an existing config map for a given entity. * Prefer this method over `get` when possible. * * It automatically names the resource with the following format: `{clusterName}.{namespace}.{name}.{clusterId}`. * * This method is idempotent and will return the same instance for the same entity. * * @param entity The entity to get the config map for. * @param cluster The cluster where the config map is located. */ static for(entity: k8s.ScopedResource, cluster: Input): ConfigMap { return getOrCreate( ConfigMap.configMapCache, `${entity.clusterName}.${entity.metadata.namespace}.${entity.metadata.name}.${entity.clusterId}`, name => { return ConfigMap.get(name, { name: entity.metadata.name, namespace: Namespace.forResource(entity, cluster), }) }, ) } /** * Gets an existing config map for a given entity. * Prefer this method over `get` when possible. * * It automatically names the resource with the following format: `{clusterName}.{namespace}.{name}.{clusterId}`. * * This method is idempotent and will return the same instance for the same entity. * * @param entity The entity to get the config map for. * @param cluster The cluster where the config map is located. */ static async forAsync( entity: Input, cluster: Input, ): Promise { const resolvedEntity = await toPromise(entity) return ConfigMap.for(resolvedEntity, cluster) } } class CreatedConfigMap extends ConfigMap { constructor(name: string, args: ConfigMapArgs, opts?: ComponentResourceOptions) { const configMap = output(args.namespace).cluster.apply(cluster => { return new core.v1.ConfigMap( name, { metadata: mapMetadata(args, name), data: args.data, }, { ...opts, parent: this, provider: getProvider(cluster), }, ) }) super( "highstate:k8s:ConfigMap", name, args, opts, configMap.apiVersion, configMap.kind, output(args.namespace), configMap.metadata, configMap.data, ) } } class ConfigMapPatch extends ConfigMap { constructor(name: string, args: ConfigMapArgs, opts?: ComponentResourceOptions) { const configMap = output(args.namespace).cluster.apply(cluster => { return new core.v1.ConfigMapPatch( name, { metadata: mapMetadata(args, name), data: args.data, }, { ...opts, parent: this, provider: getProvider(cluster), }, ) }) super( "highstate:k8s:ConfigMapPatch", name, args, opts, configMap.apiVersion, configMap.kind, output(args.namespace), configMap.metadata, configMap.data, ) } } export type WrappedConfigMapArgs = { /** * The underlying Kubernetes config map to wrap. */ configMap: Input /** * The namespace where the config map is located. */ namespace: Input } class WrappedConfigMap extends ConfigMap { constructor(name: string, args: WrappedConfigMapArgs, opts?: ComponentResourceOptions) { super( "highstate:k8s:WrappedConfigMap", name, args, opts, output(args.configMap).apiVersion, output(args.configMap).kind, output(args.namespace), output(args.configMap).metadata, output(args.configMap).data, ) } } export type ExternalConfigMapArgs = { /** * The name of the config map to get. */ name: Input /** * The namespace where the config map is located. */ namespace: Input } class ExternalConfigMap extends ConfigMap { constructor(name: string, args: ExternalConfigMapArgs, opts?: ComponentResourceOptions) { const configMap = output(args.namespace).cluster.apply(cluster => { return core.v1.ConfigMap.get( name, interpolate`${output(args.namespace).metadata.name}/${args.name}`, { ...opts, parent: this, provider: getProvider(cluster) }, ) }) super( "highstate:k8s:ExternalConfigMap", name, args, opts, configMap.apiVersion, configMap.kind, output(args.namespace), configMap.metadata, configMap.data, ) } }