@highstate/k8s 0.9.9 → 0.9.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-7R2VAXVL.js → chunk-5S4JPM4M.js} +4 -3
- package/dist/chunk-5S4JPM4M.js.map +1 -0
- package/dist/{chunk-W72HEBHG.js → chunk-6L67WIZW.js} +3 -3
- package/dist/{chunk-L6G2IHDP.js → chunk-SARVLQZY.js} +218 -45
- package/dist/chunk-SARVLQZY.js.map +1 -0
- package/dist/{chunk-WUJ7BFVE.js → chunk-VL7Z5FJQ.js} +3 -3
- package/dist/{chunk-OP75IMU7.js → chunk-WEKIQRCZ.js} +43 -17
- package/dist/chunk-WEKIQRCZ.js.map +1 -0
- package/dist/{chunk-HTQP2NB4.js → chunk-Y3LZSX7I.js} +4 -17
- package/dist/chunk-Y3LZSX7I.js.map +1 -0
- package/dist/deployment-QTPBNKO5.js +10 -0
- package/dist/highstate.manifest.json +8 -8
- package/dist/index.js +29 -41
- package/dist/index.js.map +1 -1
- package/dist/stateful-set-K4GV7ZTK.js +10 -0
- package/dist/units/cert-manager/index.js +3 -3
- package/dist/units/dns01-issuer/index.js +1 -1
- package/dist/units/gateway-api/index.js +1 -1
- package/package.json +9 -9
- package/src/config-map.ts +180 -0
- package/src/container.ts +48 -2
- package/src/cron-job.ts +8 -1
- package/src/custom.ts +104 -0
- package/src/helm.ts +2 -1
- package/src/index.ts +1 -2
- package/src/job.ts +8 -1
- package/src/network-policy.ts +23 -21
- package/src/network.ts +6 -6
- package/src/scripting/bundle.ts +7 -5
- package/src/secret.ts +4 -0
- package/src/service.ts +8 -8
- package/src/shared.ts +7 -19
- package/src/workload.ts +50 -28
- package/dist/chunk-7R2VAXVL.js.map +0 -1
- package/dist/chunk-HTQP2NB4.js.map +0 -1
- package/dist/chunk-L6G2IHDP.js.map +0 -1
- package/dist/chunk-OP75IMU7.js.map +0 -1
- package/dist/deployment-A26RVQ73.js +0 -10
- package/dist/stateful-set-S5BHTDJY.js +0 -10
- /package/dist/{chunk-W72HEBHG.js.map → chunk-6L67WIZW.js.map} +0 -0
- /package/dist/{chunk-WUJ7BFVE.js.map → chunk-VL7Z5FJQ.js.map} +0 -0
- /package/dist/{deployment-A26RVQ73.js.map → deployment-QTPBNKO5.js.map} +0 -0
- /package/dist/{stateful-set-S5BHTDJY.js.map → stateful-set-K4GV7ZTK.js.map} +0 -0
package/dist/index.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/access-point.ts","../src/scripting/bundle.ts","../src/scripting/environment.ts","../src/scripting/container.ts","../src/job.ts","../src/cron-job.ts","../src/network.ts"],"sourcesContent":["import type { k8s } from \"@highstate/library\"\nimport type { Provider } from \"@pulumi/kubernetes\"\nimport type { Namespace } from \"./namespace\"\nimport { DnsRecordSet, filterEndpoints, l3EndpointToString } from \"@highstate/common\"\nimport { gateway } from \"@highstate/gateway-api\"\nimport {\n normalize,\n Output,\n output,\n toPromise,\n type Input,\n type InputArray,\n} from \"@highstate/pulumi\"\nimport { NetworkPolicy } from \"./network-policy\"\nimport { getProvider, mapNamespaceLikeToNamespaceName } from \"./shared\"\nimport { isFromCluster } from \"./service\"\n\nexport type UseAccessPointResult = {\n /**\n * The gateway instance created according to the access point.\n */\n gateway: gateway.v1.Gateway\n\n /**\n * The DNS record sets associated created according to the access point and gateway.\n */\n dnsRecordSets: DnsRecordSet[]\n\n /**\n * The network policies associated with the access point.\n */\n networkPolicies: NetworkPolicy[]\n}\n\nexport type UseAccessPointArgs = Omit<CreateGatewayArgs, \"gateway\"> & {\n accessPoint: Input<k8s.AccessPoint>\n}\n\nexport function useAccessPoint(args: UseAccessPointArgs): Promise<UseAccessPointResult> {\n const result = output({ args, namespaceName: output(args.namespace).metadata.name }).apply(\n ({ args, namespaceName }) => {\n if (args.accessPoint.clusterId !== args.cluster.id) {\n throw new Error(\n \"The provided Kubernetes cluster is different from the one where the access point is deployed.\",\n )\n }\n\n const gateway = createGateway({\n ...args,\n annotations: {\n \"cert-manager.io/cluster-issuer\": args.accessPoint.tlsIssuer.clusterIssuerName,\n },\n gateway: args.accessPoint.gateway,\n })\n\n const dnsRecordSets = normalize(args.fqdn, args.fqdns).flatMap(fqdn => {\n return DnsRecordSet.create(fqdn, {\n providers: args.accessPoint.dnsProviders,\n values: filterEndpoints(\n args.accessPoint.gateway.endpoints.filter(endpoint => endpoint.type !== \"hostname\"),\n ),\n })\n })\n\n const networkPolicies: Output<NetworkPolicy>[] = [\n NetworkPolicy.create(\n `allow-ingress-from-${l3EndpointToString(args.accessPoint.gateway.endpoints[0])}`,\n {\n namespace: args.namespace,\n cluster: args.cluster,\n\n description: `Allow ingress traffic from the gateway at \"${l3EndpointToString(args.accessPoint.gateway.endpoints[0])}\".`,\n\n ingressRule: {\n fromEndpoints: args.accessPoint.gateway.endpoints,\n },\n },\n { provider: args.provider },\n ),\n ]\n\n if (isFromCluster(args.accessPoint.gateway.endpoints[0], args.cluster)) {\n networkPolicies.push(\n NetworkPolicy.create(\n `allow-egress-to-${namespaceName}`,\n {\n namespace: args.accessPoint.gateway.endpoints[0].metadata.k8sService.namespace,\n cluster: args.cluster,\n\n selector: args.accessPoint.gateway.endpoints[0].metadata.k8sService.selector,\n\n description: `Allow egress traffic to the namespace \"${namespaceName}\".`,\n\n egressRule: {\n toNamespace: args.namespace,\n },\n },\n { provider: args.provider },\n ),\n )\n }\n\n return output({\n gateway,\n dnsRecordSets,\n networkPolicies,\n })\n },\n )\n\n return toPromise(result)\n}\n\nexport type StandardAccessPointArgs = {\n appName: string\n fqdn: string\n}\n\nexport type StandardAccessPointInputs = {\n accessPoint: Output<k8s.AccessPoint>\n k8sCluster: Output<k8s.Cluster>\n}\n\nexport async function useStandardAcessPoint(\n namespace: Namespace,\n args: StandardAccessPointArgs,\n inputs: StandardAccessPointInputs,\n): Promise<UseAccessPointResult> {\n return await useAccessPoint({\n name: args.appName,\n namespace,\n\n fqdn: args.fqdn,\n\n accessPoint: inputs.accessPoint,\n cluster: inputs.k8sCluster,\n provider: await getProvider(inputs.k8sCluster),\n })\n}\n\nexport type CreateGatewayArgs = {\n name: string\n namespace: Input<Namespace>\n annotations?: Input<Record<string, string>>\n\n fqdn?: Input<string>\n fqdns?: InputArray<string>\n\n gateway: Input<k8s.Gateway>\n cluster: Input<k8s.Cluster>\n provider: Provider\n}\n\nexport function createGateway(args: CreateGatewayArgs): Output<gateway.v1.Gateway> {\n return output(args).apply(args => {\n if (args.cluster.id !== args.gateway.clusterId) {\n throw new Error(\n \"The provided Kubernetes cluster is different from the one where the gateway controller is deployed.\",\n )\n }\n\n return new gateway.v1.Gateway(\n args.name,\n {\n metadata: {\n name: args.name,\n namespace: mapNamespaceLikeToNamespaceName(args.namespace),\n annotations: args.annotations,\n },\n spec: {\n gatewayClassName: output(args.gateway).gatewayClassName,\n listeners: normalize(args.fqdn, args.fqdns).map(fqdn => {\n const normalizedName = fqdn.replace(/\\*/g, \"wildcard\")\n\n return {\n name: `https-${normalizedName}`,\n port: output(args.gateway).httpsListenerPort,\n protocol: \"HTTPS\",\n hostname: fqdn,\n tls: {\n mode: \"Terminate\",\n certificateRefs: [{ name: normalizedName }],\n },\n }\n }),\n },\n },\n { provider: args.provider, deletedWith: args.namespace },\n )\n })\n}\n","import type { ContainerEnvironment, ContainerVolumeMount, WorkloadVolume } from \"../container\"\nimport type { network } from \"@highstate/library\"\nimport { core } from \"@pulumi/kubernetes\"\nimport { apply, normalize, type InputArray } from \"@highstate/pulumi\"\nimport {\n ComponentResource,\n output,\n type ComponentResourceOptions,\n type Input,\n type Output,\n type Unwrap,\n} from \"@pulumi/pulumi\"\nimport { mapValues, omitBy, pipe } from \"remeda\"\nimport { deepmerge } from \"deepmerge-ts\"\nimport { readPackageJSON } from \"pkg-types\"\nimport { text, trimIndentation } from \"@highstate/contract\"\nimport { parseL34Endpoint } from \"@highstate/common\"\nimport { serializeFunction } from \"@pulumi/pulumi/runtime/index.js\"\nimport { mapMetadata, type CommonArgs } from \"../shared\"\nimport {\n emptyScriptEnvironment,\n functionScriptImages,\n type ResolvedScriptEnvironment,\n type ScriptDistribution,\n type ScriptEnvironment,\n} from \"./environment\"\n\nexport type ScriptBundleArgs = CommonArgs & {\n /**\n * The environment to bundle the scripts from.\n */\n environment?: Input<ScriptEnvironment>\n\n /**\n * The environments to bundle the scripts from.\n */\n environments?: InputArray<ScriptEnvironment>\n\n /**\n * The distribution to use for the scripts.\n */\n distribution: ScriptDistribution\n}\n\nexport class ScriptBundle extends ComponentResource {\n /**\n * The config map containing the scripts.\n */\n readonly configMap: Output<core.v1.ConfigMap>\n\n /**\n * The volumes that should be included in the workload.\n */\n readonly volumes: Output<WorkloadVolume[]>\n\n /**\n * The volume mounts that should be defined in the container.\n */\n readonly volumeMounts: Output<ContainerVolumeMount[]>\n\n /**\n * The environment variables that should be defined in the container.\n */\n readonly environment: Output<ContainerEnvironment>\n\n /**\n * The image to use for the scripts.\n */\n readonly image: Output<string>\n\n /**\n * The distribution to use for the scripts.\n */\n readonly distribution: ScriptDistribution\n\n /**\n * The list of endpoints that the script is allowed to access.\n */\n readonly allowedEndpoints: Output<network.L34Endpoint[]>\n\n constructor(name: string, args: ScriptBundleArgs, opts?: ComponentResourceOptions) {\n super(\"highstate:k8s:ScriptBundle\", name, args, opts)\n\n const scriptEnvironment = pipe(\n output(args),\n apply(args => normalize(args.environment, args.environments)),\n apply(args => deepmerge(emptyScriptEnvironment, ...args)),\n ) as Output<Unwrap<ResolvedScriptEnvironment>>\n\n const hasFunctionScripts = scriptEnvironment.apply(scriptEnvironment => {\n return Object.values(scriptEnvironment.files).some(file => typeof file === \"function\")\n })\n\n this.distribution = args.distribution\n this.environment = scriptEnvironment.environment\n\n this.image = hasFunctionScripts.apply(hasFunctionScripts =>\n output(\n hasFunctionScripts\n ? functionScriptImages[args.distribution]\n : scriptEnvironment[args.distribution].image,\n ),\n )\n\n this.allowedEndpoints = output({ scriptEnvironment, hasFunctionScripts }).apply(\n ({ scriptEnvironment, hasFunctionScripts }) => {\n const allowedEndpoints = [\n ...scriptEnvironment.allowedEndpoints,\n ...scriptEnvironment[args.distribution].allowedEndpoints,\n ]\n\n if (hasFunctionScripts) {\n allowedEndpoints.push(\"tcp://registry.npmjs.org:443\")\n }\n\n return allowedEndpoints.map(parseL34Endpoint)\n },\n )\n\n this.configMap = output({ scriptEnvironment, args }).apply(({ scriptEnvironment, args }) => {\n return new core.v1.ConfigMap(\n name,\n {\n metadata: mapMetadata(args, name),\n data: createScriptData(this.distribution, scriptEnvironment),\n },\n { ...opts, parent: this },\n )\n })\n\n this.volumes = output({ hasFunctionScripts, volumes: scriptEnvironment.volumes }).apply(\n ({ hasFunctionScripts, volumes }) => {\n return [\n ...volumes,\n {\n name: this.configMap.metadata.name,\n\n configMap: {\n name: this.configMap.metadata.name,\n defaultMode: 0o550, // read and execute permissions\n },\n },\n ...(hasFunctionScripts ? [{ name: \"node-modules\", emptyDir: {} }] : []),\n ]\n },\n )\n\n this.volumeMounts = output({\n hasFunctionScripts,\n volumeMounts: scriptEnvironment.volumeMounts,\n }).apply(({ hasFunctionScripts, volumeMounts }) => {\n return [\n ...volumeMounts,\n {\n volume: this.configMap,\n mountPath: \"/scripts\",\n },\n ...(hasFunctionScripts\n ? [{ name: \"node-modules\", mountPath: \"/scripts/node_modules\" }]\n : []),\n ]\n })\n\n this.registerOutputs({\n configMap: this.configMap,\n volumes: this.volumes,\n volumeMounts: this.volumeMounts,\n environment: this.environment,\n distribution: this.distribution,\n allowedEndpoints: this.allowedEndpoints,\n image: this.image,\n })\n }\n}\n\nfunction stripWorkspacePrefix(value: string): string {\n if (value.startsWith(\"workspace:\")) {\n return value.replace(\"workspace:\", \"\")\n }\n\n return value\n}\n\nasync function createScriptData(\n distribution: ScriptDistribution,\n environment: Unwrap<ResolvedScriptEnvironment>,\n): Promise<Record<string, string>> {\n const scriptData: Record<string, string> = {}\n const actions: string[] = []\n\n const distributionEnvironment = environment[distribution]\n const setupScripts = { ...environment.setupScripts }\n\n let hasFunctionScripts = false\n\n for (const key in environment.files) {\n if (typeof environment.files[key] === \"function\") {\n const serialized = await serializeFunction(environment.files[key])\n\n scriptData[key] = text`\n #!/usr/local/bin/bun\n \n ${serialized.text}\n\n exports.${serialized.exportName}()\n `\n\n hasFunctionScripts = true\n } else {\n scriptData[key] = environment.files[key]\n }\n }\n\n if (hasFunctionScripts) {\n const packageJson = await readPackageJSON()\n\n packageJson.dependencies = omitBy(\n mapValues(packageJson.dependencies ?? {}, stripWorkspacePrefix),\n (_, key) => key.startsWith(\"@highstate/\"),\n )\n\n packageJson.devDependencies = omitBy(\n mapValues(packageJson.devDependencies ?? {}, stripWorkspacePrefix),\n (_, key) => key.startsWith(\"@highstate/\"),\n )\n\n scriptData[\"package.json\"] = JSON.stringify(packageJson, null, 2)\n\n setupScripts[\"resolve-dependencies.sh\"] = text`\n #!/usr/local/bin/bun\n set -e\n\n cd /scripts\n bun install --production\n `\n }\n\n if (distributionEnvironment.preInstallPackages.length > 0) {\n scriptData[\"pre-install-packages.sh\"] = getInstallPackagesScript(\n distribution,\n distributionEnvironment.preInstallPackages,\n )\n\n actions.push(`\n echo \"+ Installing pre-install packages...\"\n /scripts/pre-install-packages.sh\n echo \"+ Pre-install packages installed successfully\"\n `)\n }\n\n if (Object.keys(distributionEnvironment.preInstallScripts).length > 0) {\n for (const key in distributionEnvironment.preInstallScripts) {\n scriptData[`pre-install-${key}`] = distributionEnvironment.preInstallScripts[key]\n\n actions.push(`\n echo \"+ Running pre-install script '${key}'...\"\n /scripts/pre-install-${key}\n echo \"+ Pre-install script '${key}'... Done\"\n `)\n }\n }\n\n if (distributionEnvironment.packages.length > 0) {\n scriptData[\"install-packages.sh\"] = getInstallPackagesScript(\n distribution,\n distributionEnvironment.packages,\n )\n\n actions.push(`\n echo \"+ Installing packages...\"\n /scripts/install-packages.sh\n echo \"+ Packages installed successfully\"\n `)\n }\n\n if (Object.keys(setupScripts).length > 0) {\n for (const key in setupScripts) {\n scriptData[`setup-${key}`] = setupScripts[key]\n\n actions.push(`\n echo \"+ Running setup script '${key}'...\"\n /scripts/setup-${key}\n echo \"+ Setup script '${key}'... Done\"\n `)\n }\n }\n\n if (Object.keys(environment.cleanupScripts).length > 0) {\n const cleanupActions: string[] = []\n\n for (const key in environment.cleanupScripts) {\n scriptData[`cleanup-${key}`] = environment.cleanupScripts[key]\n\n cleanupActions.push(`\n echo \"+ Running cleanup script '${key}'...\"\n /scripts/cleanup-${key}\n echo \"+ Cleanup script '${key}'... Done\"\n `)\n }\n\n actions.push(`\n function cleanup() {\n ${cleanupActions.map(s => s.trim()).join(\"\\n\\n\")}\n }\n\n trap cleanup EXIT\n trap cleanup SIGTERM\n `)\n }\n\n scriptData[\"entrypoint.sh\"] = trimIndentation(`\n #!/bin/sh\n set -e\n\n if [ -z \"$1\" ]; then\n echo \"Usage: entrypoint.sh <main script> [args...]\"\n exit 1\n fi\n\n ${actions.map(s => s.trim()).join(\"\\n\\n\")}\n\n echo \"+ Running main script...\"\n $@\n echo \"+ Main script completed\"\n `)\n\n return scriptData\n}\n\nfunction getInstallPackagesScript(distribution: ScriptDistribution, packages: string[]): string {\n if (distribution === \"alpine\") {\n return text`\n #!/bin/sh\n set -e\n\n apk add --no-cache ${packages.join(\" \")}\n `\n } else {\n return text`\n #!/bin/sh\n set -e\n\n apt-get update\n apt-get install -y ${packages.join(\" \")}\n `\n }\n}\n","import type { Input, InputArray, InputMap } from \"@highstate/pulumi\"\nimport type { ContainerEnvironment, ContainerVolumeMount, WorkloadVolume } from \"../container\"\nimport type { InputL34Endpoint } from \"@highstate/common\"\n\nexport type ScriptDistribution = \"alpine\" | \"ubuntu\"\n\nexport type DistributionEnvironment = {\n /**\n * The image that should be used for the distribution.\n */\n image?: Input<string>\n\n /**\n * The utility packages that should be installed before running \"preInstallScripts\".\n *\n * Useful for installing tools like `curl` to install additional repositories.\n */\n preInstallPackages?: InputArray<string>\n\n /**\n * The pre-install scripts that should be run before installing packages.\n * Typically, these scripts are used to install additional repositories.\n */\n preInstallScripts?: InputMap<string>\n\n /**\n * The packages that are available in the environment.\n */\n packages?: InputArray<string>\n\n /**\n * The endpoint which the script is allowed to access scoped to the distribution.\n *\n * Typically, this is used to allow access to the package manager.\n *\n * Will be used to generate a network policy.\n */\n allowedEndpoints?: InputArray<InputL34Endpoint>\n}\n\nexport type ScriptProgram = () => unknown\n\nexport type ScriptEnvironment = {\n [distribution in ScriptDistribution]?: DistributionEnvironment\n} & {\n /**\n * The setup scripts that should be run before the script.\n */\n setupScripts?: InputMap<string>\n\n /**\n * The cleanup scripts that should be run after the script.\n */\n cleanupScripts?: InputMap<string>\n\n /**\n * The arbitrary files available in the environment including scripts.\n */\n files?: InputMap<string | ScriptProgram>\n\n /**\n * The volumes that should be defined in the environment.\n */\n volumes?: InputArray<WorkloadVolume>\n\n /**\n * The volume mounts that should be defined in the environment.\n */\n volumeMounts?: InputArray<ContainerVolumeMount>\n\n /**\n * The environment variables that should be defined in the environment.\n */\n environment?: Input<ContainerEnvironment>\n\n /**\n * The endpoint which the script is allowed to access.\n *\n * Will be used to generate a network policy.\n */\n allowedEndpoints?: InputArray<InputL34Endpoint>\n}\n\nexport type ResolvedScriptEnvironment = Omit<Required<ScriptEnvironment>, ScriptDistribution> & {\n [distribution in ScriptDistribution]: Required<DistributionEnvironment>\n}\n\nconst emptyDistributionEnvironment = {\n preInstallPackages: [],\n preInstallScripts: {},\n packages: [],\n}\n\nexport const emptyScriptEnvironment: ResolvedScriptEnvironment = {\n alpine: {\n ...emptyDistributionEnvironment,\n image: \"alpine@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c\",\n allowedEndpoints: [\n //\n \"tcp://dl-cdn.alpinelinux.org:443\",\n \"tcp://dl-cdn.alpinelinux.org:80\",\n ],\n },\n\n ubuntu: {\n ...emptyDistributionEnvironment,\n image: \"ubuntu@sha256:72297848456d5d37d1262630108ab308d3e9ec7ed1c3286a32fe09856619a782\",\n allowedEndpoints: [\n //\n \"tcp://archive.ubuntu.com:80\",\n \"tcp://archive.ubuntu.com:443\",\n \"tcp://security.ubuntu.com:80\",\n \"tcp://security.ubuntu.com:443\",\n ],\n },\n\n setupScripts: {},\n cleanupScripts: {},\n files: {},\n volumes: [],\n volumeMounts: [],\n environment: {},\n allowedEndpoints: [],\n}\n\nexport const functionScriptImages: Record<ScriptDistribution, string> = {\n alpine: \"oven/bun@sha256:6b14922b0885c3890cdb0b396090af1da486ba941df5ee94391eef64f7113c61\",\n ubuntu: \"oven/bun@sha256:66b431441dc4c36d7e8164bfc61e6348ec1d7ce2862fc3a29f5dc9856e8205e4\",\n}\n","import type { Container } from \"../container\"\nimport type { ScriptBundle } from \"./bundle\"\nimport { merge } from \"remeda\"\nimport { Output, output, type Input } from \"@pulumi/pulumi\"\n\nexport type ScriptContainer = Container & {\n /**\n * The script bundle to use.\n */\n bundle: Input<ScriptBundle>\n\n /**\n * The name of the main script to run.\n * The script must be available in the bundle.\n */\n main: Input<string>\n}\n\n/**\n * Creates a spec for a container that runs a script.\n * This spec can be used to create a complete workload or an init container.\n *\n * @param options The options to create the container spec.\n * @returns The container spec.\n */\nexport function createScriptContainer(options: ScriptContainer): Output<Container> {\n const bundle = output(options.bundle)\n\n return output({\n options,\n image: bundle.image,\n volumeMounts: bundle.volumeMounts,\n volumes: bundle.volumes,\n environment: bundle.environment,\n allowedEndpoints: bundle.allowedEndpoints,\n }).apply(({ options, image, volumeMounts, volumes, environment, allowedEndpoints }) => {\n return {\n image,\n command: [\"/scripts/entrypoint.sh\", `/scripts/${options.main}`],\n\n ...options,\n\n volumeMounts: [...volumeMounts, ...(options.volumeMounts ?? [])],\n volumes: [...volumes, ...(options.volumes ?? [])],\n environment: merge(environment, options.environment),\n allowedEndpoints: [...allowedEndpoints, ...(options.allowedEndpoints ?? [])],\n } as Container\n })\n}\n","import { batch, type types } from \"@pulumi/kubernetes\"\nimport { ComponentResource, Output, output, type ComponentResourceOptions } from \"@highstate/pulumi\"\nimport { mergeDeep, omit } from \"remeda\"\nimport { commonExtraArgs, getProvider, mapMetadata } from \"./shared\"\nimport { getWorkloadComponents, type WorkloadArgs } from \"./workload\"\n\nexport type JobArgs = WorkloadArgs &\n Omit<Partial<types.input.batch.v1.JobSpec>, \"template\"> & {\n template?: {\n metadata?: types.input.meta.v1.ObjectMeta\n spec?: Partial<types.input.core.v1.PodSpec>\n }\n }\n\nconst jobExtraArgs = [...commonExtraArgs, \"container\", \"containers\"] as const\n\nexport class Job extends ComponentResource {\n /**\n * The underlying Kubernetes job.\n */\n public readonly job: Output<batch.v1.Job>\n\n constructor(name: string, args: JobArgs, opts: ComponentResourceOptions) {\n super(\"highstate:k8s:Job\", name, args, opts)\n\n const { podTemplate } = getWorkloadComponents(name, args, () => this, opts)\n\n this.job = output({ args, podTemplate }).apply(async ({ args, podTemplate }) => {\n return new batch.v1.Job(\n name,\n {\n metadata: mapMetadata(args, name),\n spec: mergeDeep(\n {\n template: podTemplate,\n } satisfies types.input.batch.v1.JobSpec,\n omit(args, jobExtraArgs) as types.input.batch.v1.JobSpec,\n ),\n },\n {\n ...opts,\n parent: this,\n provider: await getProvider(args.cluster),\n },\n )\n })\n }\n}\n","import type { RequiredKeys } from \"@highstate/contract\"\nimport { batch, type types } from \"@pulumi/kubernetes\"\nimport { ComponentResource, Output, output, type ComponentResourceOptions } from \"@highstate/pulumi\"\nimport { mergeDeep, omit } from \"remeda\"\nimport { commonExtraArgs, getProvider, mapMetadata } from \"./shared\"\nimport { getWorkloadComponents, type WorkloadArgs } from \"./workload\"\n\nexport type CronJobArgs = WorkloadArgs &\n Omit<RequiredKeys<Partial<types.input.batch.v1.CronJobSpec>, \"schedule\">, \"jobTemplate\"> & {\n jobTemplate?: {\n metadata?: types.input.meta.v1.ObjectMeta\n spec?: Omit<types.input.batch.v1.JobSpec, \"template\"> & {\n template?: {\n metadata?: types.input.meta.v1.ObjectMeta\n spec?: Partial<types.input.core.v1.PodSpec>\n }\n }\n }\n }\n\nconst cronJobExtraArgs = [...commonExtraArgs, \"container\", \"containers\"] as const\n\nexport class CronJob extends ComponentResource {\n /**\n * The underlying Kubernetes job.\n */\n public readonly cronJob: Output<batch.v1.CronJob>\n\n constructor(name: string, args: CronJobArgs, opts: ComponentResourceOptions) {\n super(\"highstate:k8s:CronJob\", name, args, opts)\n\n const { podTemplate } = getWorkloadComponents(name, args, () => this, opts)\n\n this.cronJob = output({ args, podTemplate }).apply(async ({ args, podTemplate }) => {\n return new batch.v1.CronJob(\n name,\n {\n metadata: mapMetadata(args, name),\n\n spec: mergeDeep(\n {\n jobTemplate: {\n spec: {\n template: podTemplate,\n },\n },\n\n schedule: args.schedule,\n } satisfies types.input.batch.v1.CronJobSpec,\n omit(args, cronJobExtraArgs) as types.input.batch.v1.CronJobSpec,\n ),\n },\n {\n ...opts,\n parent: this,\n provider: await getProvider(args.cluster),\n },\n )\n })\n }\n}\n","import type { k8s, network } from \"@highstate/library\"\nimport { filterEndpoints } from \"@highstate/common\"\nimport { isFromCluster } from \"./service\"\n\nexport function getBestEndpoint(\n endpoints: network.L4Endpoint[],\n cluster?: k8s.Cluster,\n): network.L4Endpoint | undefined {\n if (!endpoints.length) {\n return undefined\n }\n\n if (endpoints.length === 1) {\n return endpoints[0]\n }\n\n if (!cluster) {\n return filterEndpoints(endpoints)[0]\n }\n\n const clusterEndpoint = endpoints.find(endpoint => isFromCluster(endpoint, cluster))\n\n if (clusterEndpoint) {\n return clusterEndpoint\n }\n\n return filterEndpoints(endpoints)[0]\n}\n\nexport function requireBestEndpoint(\n endpoints: network.L4Endpoint[],\n cluster: k8s.Cluster,\n): network.L4Endpoint {\n const endpoint = getBestEndpoint(endpoints, cluster)\n\n if (!endpoint) {\n throw new Error(`No best endpoint found for cluster \"${cluster.name}\" (${cluster.id})`)\n }\n\n return endpoint\n}\n"],"mappings":"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","names":["args","gateway","normalize","output","output","args","normalize","scriptEnvironment","hasFunctionScripts","output","options","ComponentResource","output","ComponentResource","output","args","podTemplate","batch","ComponentResource","output","mergeDeep","omit","ComponentResource","output","args","podTemplate","batch","mergeDeep","omit","filterEndpoints","filterEndpoints"]}
|
1
|
+
{"version":3,"sources":["../src/access-point.ts","../src/scripting/bundle.ts","../src/scripting/environment.ts","../src/scripting/container.ts","../src/job.ts","../src/cron-job.ts"],"sourcesContent":["import type { k8s } from \"@highstate/library\"\nimport type { Provider } from \"@pulumi/kubernetes\"\nimport type { Namespace } from \"./namespace\"\nimport { DnsRecordSet, filterEndpoints, l3EndpointToString } from \"@highstate/common\"\nimport { gateway } from \"@highstate/gateway-api\"\nimport {\n normalize,\n Output,\n output,\n toPromise,\n type Input,\n type InputArray,\n} from \"@highstate/pulumi\"\nimport { NetworkPolicy } from \"./network-policy\"\nimport { getProvider, mapNamespaceLikeToNamespaceName } from \"./shared\"\nimport { isFromCluster } from \"./service\"\n\nexport type UseAccessPointResult = {\n /**\n * The gateway instance created according to the access point.\n */\n gateway: gateway.v1.Gateway\n\n /**\n * The DNS record sets associated created according to the access point and gateway.\n */\n dnsRecordSets: DnsRecordSet[]\n\n /**\n * The network policies associated with the access point.\n */\n networkPolicies: NetworkPolicy[]\n}\n\nexport type UseAccessPointArgs = Omit<CreateGatewayArgs, \"gateway\"> & {\n accessPoint: Input<k8s.AccessPoint>\n}\n\nexport function useAccessPoint(args: UseAccessPointArgs): Promise<UseAccessPointResult> {\n const result = output({ args, namespaceName: output(args.namespace).metadata.name }).apply(\n ({ args, namespaceName }) => {\n if (args.accessPoint.clusterId !== args.cluster.id) {\n throw new Error(\n \"The provided Kubernetes cluster is different from the one where the access point is deployed.\",\n )\n }\n\n const gateway = createGateway({\n ...args,\n annotations: {\n \"cert-manager.io/cluster-issuer\": args.accessPoint.tlsIssuer.clusterIssuerName,\n },\n gateway: args.accessPoint.gateway,\n })\n\n const dnsRecordSets = normalize(args.fqdn, args.fqdns).flatMap(fqdn => {\n return DnsRecordSet.create(fqdn, {\n providers: args.accessPoint.dnsProviders,\n values: filterEndpoints(\n args.accessPoint.gateway.endpoints.filter(endpoint => endpoint.type !== \"hostname\"),\n ),\n })\n })\n\n const networkPolicies: Output<NetworkPolicy>[] = [\n NetworkPolicy.create(\n `allow-ingress-from-${l3EndpointToString(args.accessPoint.gateway.endpoints[0])}`,\n {\n namespace: args.namespace,\n cluster: args.cluster,\n\n description: `Allow ingress traffic from the gateway at \"${l3EndpointToString(args.accessPoint.gateway.endpoints[0])}\".`,\n\n ingressRule: {\n fromEndpoints: args.accessPoint.gateway.endpoints,\n },\n },\n { provider: args.provider },\n ),\n ]\n\n if (isFromCluster(args.accessPoint.gateway.endpoints[0], args.cluster)) {\n networkPolicies.push(\n NetworkPolicy.create(\n `allow-egress-to-${namespaceName}`,\n {\n namespace: args.accessPoint.gateway.endpoints[0].metadata.k8sService.namespace,\n cluster: args.cluster,\n\n selector: args.accessPoint.gateway.endpoints[0].metadata.k8sService.selector,\n\n description: `Allow egress traffic to the namespace \"${namespaceName}\".`,\n\n egressRule: {\n toNamespace: args.namespace,\n },\n },\n { provider: args.provider },\n ),\n )\n }\n\n return output({\n gateway,\n dnsRecordSets,\n networkPolicies,\n })\n },\n )\n\n return toPromise(result)\n}\n\nexport type StandardAccessPointArgs = {\n appName: string\n fqdn: string\n}\n\nexport type StandardAccessPointInputs = {\n accessPoint: Output<k8s.AccessPoint>\n k8sCluster: Output<k8s.Cluster>\n}\n\nexport async function useStandardAcessPoint(\n namespace: Namespace,\n args: StandardAccessPointArgs,\n inputs: StandardAccessPointInputs,\n): Promise<UseAccessPointResult> {\n return await useAccessPoint({\n name: args.appName,\n namespace,\n\n fqdn: args.fqdn,\n\n accessPoint: inputs.accessPoint,\n cluster: inputs.k8sCluster,\n provider: await getProvider(inputs.k8sCluster),\n })\n}\n\nexport type CreateGatewayArgs = {\n name: string\n namespace: Input<Namespace>\n annotations?: Input<Record<string, string>>\n\n fqdn?: Input<string>\n fqdns?: InputArray<string>\n\n gateway: Input<k8s.Gateway>\n cluster: Input<k8s.Cluster>\n provider: Provider\n}\n\nexport function createGateway(args: CreateGatewayArgs): Output<gateway.v1.Gateway> {\n return output(args).apply(args => {\n if (args.cluster.id !== args.gateway.clusterId) {\n throw new Error(\n \"The provided Kubernetes cluster is different from the one where the gateway controller is deployed.\",\n )\n }\n\n return new gateway.v1.Gateway(\n args.name,\n {\n metadata: {\n name: args.name,\n namespace: mapNamespaceLikeToNamespaceName(args.namespace),\n annotations: args.annotations,\n },\n spec: {\n gatewayClassName: output(args.gateway).gatewayClassName,\n listeners: normalize(args.fqdn, args.fqdns).map(fqdn => {\n const normalizedName = fqdn.replace(/\\*/g, \"wildcard\")\n\n return {\n name: `https-${normalizedName}`,\n port: output(args.gateway).httpsListenerPort,\n protocol: \"HTTPS\",\n hostname: fqdn,\n tls: {\n mode: \"Terminate\",\n certificateRefs: [{ name: normalizedName }],\n },\n }\n }),\n },\n },\n { provider: args.provider, deletedWith: args.namespace },\n )\n })\n}\n","import type { ContainerEnvironment, ContainerVolumeMount, WorkloadVolume } from \"../container\"\nimport type { network } from \"@highstate/library\"\nimport { apply, normalize, type InputArray } from \"@highstate/pulumi\"\nimport {\n ComponentResource,\n output,\n type ComponentResourceOptions,\n type Input,\n type Output,\n type Unwrap,\n} from \"@pulumi/pulumi\"\nimport { mapValues, omitBy, pipe } from \"remeda\"\nimport { deepmerge } from \"deepmerge-ts\"\nimport { readPackageJSON } from \"pkg-types\"\nimport { text, trimIndentation } from \"@highstate/contract\"\nimport { parseL34Endpoint } from \"@highstate/common\"\nimport { serializeFunction } from \"@pulumi/pulumi/runtime/index.js\"\nimport { type CommonArgs } from \"../shared\"\nimport { ConfigMap } from \"../config-map\"\nimport {\n emptyScriptEnvironment,\n functionScriptImages,\n type ResolvedScriptEnvironment,\n type ScriptDistribution,\n type ScriptEnvironment,\n} from \"./environment\"\n\nexport type ScriptBundleArgs = CommonArgs & {\n /**\n * The environment to bundle the scripts from.\n */\n environment?: Input<ScriptEnvironment>\n\n /**\n * The environments to bundle the scripts from.\n */\n environments?: InputArray<ScriptEnvironment>\n\n /**\n * The distribution to use for the scripts.\n */\n distribution: ScriptDistribution\n}\n\nexport class ScriptBundle extends ComponentResource {\n /**\n * The config map containing the scripts.\n */\n readonly configMap: Output<ConfigMap>\n\n /**\n * The volumes that should be included in the workload.\n */\n readonly volumes: Output<WorkloadVolume[]>\n\n /**\n * The volume mounts that should be defined in the container.\n */\n readonly volumeMounts: Output<ContainerVolumeMount[]>\n\n /**\n * The environment variables that should be defined in the container.\n */\n readonly environment: Output<ContainerEnvironment>\n\n /**\n * The image to use for the scripts.\n */\n readonly image: Output<string>\n\n /**\n * The distribution to use for the scripts.\n */\n readonly distribution: ScriptDistribution\n\n /**\n * The list of endpoints that the script is allowed to access.\n */\n readonly allowedEndpoints: Output<network.L34Endpoint[]>\n\n constructor(name: string, args: ScriptBundleArgs, opts?: ComponentResourceOptions) {\n super(\"highstate:k8s:ScriptBundle\", name, args, opts)\n\n const scriptEnvironment = pipe(\n output(args),\n apply(args => normalize(args.environment, args.environments)),\n apply(args => deepmerge(emptyScriptEnvironment, ...args)),\n ) as Output<Unwrap<ResolvedScriptEnvironment>>\n\n const hasFunctionScripts = scriptEnvironment.apply(scriptEnvironment => {\n return Object.values(scriptEnvironment.files).some(file => typeof file === \"function\")\n })\n\n this.distribution = args.distribution\n this.environment = scriptEnvironment.environment\n\n this.image = hasFunctionScripts.apply(hasFunctionScripts =>\n output(\n hasFunctionScripts\n ? functionScriptImages[args.distribution]\n : scriptEnvironment[args.distribution].image,\n ),\n )\n\n this.allowedEndpoints = output({ scriptEnvironment, hasFunctionScripts }).apply(\n ({ scriptEnvironment, hasFunctionScripts }) => {\n const allowedEndpoints = [\n ...scriptEnvironment.allowedEndpoints,\n ...scriptEnvironment[args.distribution].allowedEndpoints,\n ]\n\n if (hasFunctionScripts) {\n allowedEndpoints.push(\"tcp://registry.npmjs.org:443\")\n }\n\n return allowedEndpoints.map(parseL34Endpoint)\n },\n )\n\n this.configMap = output({ scriptEnvironment, args }).apply(({ scriptEnvironment, args }) => {\n return ConfigMap.create(\n name,\n {\n cluster: args.cluster,\n namespace: args.namespace,\n\n data: createScriptData(this.distribution, scriptEnvironment),\n },\n { ...opts, parent: this },\n )\n })\n\n this.volumes = output({ hasFunctionScripts, volumes: scriptEnvironment.volumes }).apply(\n ({ hasFunctionScripts, volumes }) => {\n return [\n ...volumes,\n {\n name: this.configMap.metadata.name,\n\n configMap: {\n name: this.configMap.metadata.name,\n defaultMode: 0o550, // read and execute permissions\n },\n },\n ...(hasFunctionScripts ? [{ name: \"node-modules\", emptyDir: {} }] : []),\n ]\n },\n )\n\n this.volumeMounts = output({\n hasFunctionScripts,\n volumeMounts: scriptEnvironment.volumeMounts,\n }).apply(({ hasFunctionScripts, volumeMounts }) => {\n return [\n ...volumeMounts,\n {\n volume: this.configMap,\n mountPath: \"/scripts\",\n },\n ...(hasFunctionScripts\n ? [{ name: \"node-modules\", mountPath: \"/scripts/node_modules\" }]\n : []),\n ]\n })\n\n this.registerOutputs({\n configMap: this.configMap,\n volumes: this.volumes,\n volumeMounts: this.volumeMounts,\n environment: this.environment,\n distribution: this.distribution,\n allowedEndpoints: this.allowedEndpoints,\n image: this.image,\n })\n }\n}\n\nfunction stripWorkspacePrefix(value: string): string {\n if (value.startsWith(\"workspace:\")) {\n return value.replace(\"workspace:\", \"\")\n }\n\n return value\n}\n\nasync function createScriptData(\n distribution: ScriptDistribution,\n environment: Unwrap<ResolvedScriptEnvironment>,\n): Promise<Record<string, string>> {\n const scriptData: Record<string, string> = {}\n const actions: string[] = []\n\n const distributionEnvironment = environment[distribution]\n const setupScripts = { ...environment.setupScripts }\n\n let hasFunctionScripts = false\n\n for (const key in environment.files) {\n if (typeof environment.files[key] === \"function\") {\n const serialized = await serializeFunction(environment.files[key])\n\n scriptData[key] = text`\n #!/usr/local/bin/bun\n \n ${serialized.text}\n\n exports.${serialized.exportName}()\n `\n\n hasFunctionScripts = true\n } else {\n scriptData[key] = environment.files[key]\n }\n }\n\n if (hasFunctionScripts) {\n const packageJson = await readPackageJSON()\n\n packageJson.dependencies = omitBy(\n mapValues(packageJson.dependencies ?? {}, stripWorkspacePrefix),\n (_, key) => key.startsWith(\"@highstate/\"),\n )\n\n packageJson.devDependencies = omitBy(\n mapValues(packageJson.devDependencies ?? {}, stripWorkspacePrefix),\n (_, key) => key.startsWith(\"@highstate/\"),\n )\n\n scriptData[\"package.json\"] = JSON.stringify(packageJson, null, 2)\n\n setupScripts[\"resolve-dependencies.sh\"] = text`\n #!/usr/local/bin/bun\n set -e\n\n cd /scripts\n bun install --production\n `\n }\n\n if (distributionEnvironment.preInstallPackages.length > 0) {\n scriptData[\"pre-install-packages.sh\"] = getInstallPackagesScript(\n distribution,\n distributionEnvironment.preInstallPackages,\n )\n\n actions.push(`\n echo \"+ Installing pre-install packages...\"\n /scripts/pre-install-packages.sh\n echo \"+ Pre-install packages installed successfully\"\n `)\n }\n\n if (Object.keys(distributionEnvironment.preInstallScripts).length > 0) {\n for (const key in distributionEnvironment.preInstallScripts) {\n scriptData[`pre-install-${key}`] = distributionEnvironment.preInstallScripts[key]\n\n actions.push(`\n echo \"+ Running pre-install script '${key}'...\"\n /scripts/pre-install-${key}\n echo \"+ Pre-install script '${key}'... Done\"\n `)\n }\n }\n\n if (distributionEnvironment.packages.length > 0) {\n scriptData[\"install-packages.sh\"] = getInstallPackagesScript(\n distribution,\n distributionEnvironment.packages,\n )\n\n actions.push(`\n echo \"+ Installing packages...\"\n /scripts/install-packages.sh\n echo \"+ Packages installed successfully\"\n `)\n }\n\n if (Object.keys(setupScripts).length > 0) {\n for (const key in setupScripts) {\n scriptData[`setup-${key}`] = setupScripts[key]\n\n actions.push(`\n echo \"+ Running setup script '${key}'...\"\n /scripts/setup-${key}\n echo \"+ Setup script '${key}'... Done\"\n `)\n }\n }\n\n if (Object.keys(environment.cleanupScripts).length > 0) {\n const cleanupActions: string[] = []\n\n for (const key in environment.cleanupScripts) {\n scriptData[`cleanup-${key}`] = environment.cleanupScripts[key]\n\n cleanupActions.push(`\n echo \"+ Running cleanup script '${key}'...\"\n /scripts/cleanup-${key}\n echo \"+ Cleanup script '${key}'... Done\"\n `)\n }\n\n actions.push(`\n function cleanup() {\n ${cleanupActions.map(s => s.trim()).join(\"\\n\\n\")}\n }\n\n trap cleanup EXIT\n trap cleanup SIGTERM\n `)\n }\n\n scriptData[\"entrypoint.sh\"] = trimIndentation(`\n #!/bin/sh\n set -e\n\n if [ -z \"$1\" ]; then\n echo \"Usage: entrypoint.sh <main script> [args...]\"\n exit 1\n fi\n\n ${actions.map(s => s.trim()).join(\"\\n\\n\")}\n\n echo \"+ Running main script...\"\n $@\n echo \"+ Main script completed\"\n `)\n\n return scriptData\n}\n\nfunction getInstallPackagesScript(distribution: ScriptDistribution, packages: string[]): string {\n if (distribution === \"alpine\") {\n return text`\n #!/bin/sh\n set -e\n\n apk add --no-cache ${packages.join(\" \")}\n `\n } else {\n return text`\n #!/bin/sh\n set -e\n\n apt-get update\n apt-get install -y ${packages.join(\" \")}\n `\n }\n}\n","import type { Input, InputArray, InputMap } from \"@highstate/pulumi\"\nimport type { ContainerEnvironment, ContainerVolumeMount, WorkloadVolume } from \"../container\"\nimport type { InputL34Endpoint } from \"@highstate/common\"\n\nexport type ScriptDistribution = \"alpine\" | \"ubuntu\"\n\nexport type DistributionEnvironment = {\n /**\n * The image that should be used for the distribution.\n */\n image?: Input<string>\n\n /**\n * The utility packages that should be installed before running \"preInstallScripts\".\n *\n * Useful for installing tools like `curl` to install additional repositories.\n */\n preInstallPackages?: InputArray<string>\n\n /**\n * The pre-install scripts that should be run before installing packages.\n * Typically, these scripts are used to install additional repositories.\n */\n preInstallScripts?: InputMap<string>\n\n /**\n * The packages that are available in the environment.\n */\n packages?: InputArray<string>\n\n /**\n * The endpoint which the script is allowed to access scoped to the distribution.\n *\n * Typically, this is used to allow access to the package manager.\n *\n * Will be used to generate a network policy.\n */\n allowedEndpoints?: InputArray<InputL34Endpoint>\n}\n\nexport type ScriptProgram = () => unknown\n\nexport type ScriptEnvironment = {\n [distribution in ScriptDistribution]?: DistributionEnvironment\n} & {\n /**\n * The setup scripts that should be run before the script.\n */\n setupScripts?: InputMap<string>\n\n /**\n * The cleanup scripts that should be run after the script.\n */\n cleanupScripts?: InputMap<string>\n\n /**\n * The arbitrary files available in the environment including scripts.\n */\n files?: InputMap<string | ScriptProgram>\n\n /**\n * The volumes that should be defined in the environment.\n */\n volumes?: InputArray<WorkloadVolume>\n\n /**\n * The volume mounts that should be defined in the environment.\n */\n volumeMounts?: InputArray<ContainerVolumeMount>\n\n /**\n * The environment variables that should be defined in the environment.\n */\n environment?: Input<ContainerEnvironment>\n\n /**\n * The endpoint which the script is allowed to access.\n *\n * Will be used to generate a network policy.\n */\n allowedEndpoints?: InputArray<InputL34Endpoint>\n}\n\nexport type ResolvedScriptEnvironment = Omit<Required<ScriptEnvironment>, ScriptDistribution> & {\n [distribution in ScriptDistribution]: Required<DistributionEnvironment>\n}\n\nconst emptyDistributionEnvironment = {\n preInstallPackages: [],\n preInstallScripts: {},\n packages: [],\n}\n\nexport const emptyScriptEnvironment: ResolvedScriptEnvironment = {\n alpine: {\n ...emptyDistributionEnvironment,\n image: \"alpine@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c\",\n allowedEndpoints: [\n //\n \"tcp://dl-cdn.alpinelinux.org:443\",\n \"tcp://dl-cdn.alpinelinux.org:80\",\n ],\n },\n\n ubuntu: {\n ...emptyDistributionEnvironment,\n image: \"ubuntu@sha256:72297848456d5d37d1262630108ab308d3e9ec7ed1c3286a32fe09856619a782\",\n allowedEndpoints: [\n //\n \"tcp://archive.ubuntu.com:80\",\n \"tcp://archive.ubuntu.com:443\",\n \"tcp://security.ubuntu.com:80\",\n \"tcp://security.ubuntu.com:443\",\n ],\n },\n\n setupScripts: {},\n cleanupScripts: {},\n files: {},\n volumes: [],\n volumeMounts: [],\n environment: {},\n allowedEndpoints: [],\n}\n\nexport const functionScriptImages: Record<ScriptDistribution, string> = {\n alpine: \"oven/bun@sha256:6b14922b0885c3890cdb0b396090af1da486ba941df5ee94391eef64f7113c61\",\n ubuntu: \"oven/bun@sha256:66b431441dc4c36d7e8164bfc61e6348ec1d7ce2862fc3a29f5dc9856e8205e4\",\n}\n","import type { Container } from \"../container\"\nimport type { ScriptBundle } from \"./bundle\"\nimport { merge } from \"remeda\"\nimport { Output, output, type Input } from \"@pulumi/pulumi\"\n\nexport type ScriptContainer = Container & {\n /**\n * The script bundle to use.\n */\n bundle: Input<ScriptBundle>\n\n /**\n * The name of the main script to run.\n * The script must be available in the bundle.\n */\n main: Input<string>\n}\n\n/**\n * Creates a spec for a container that runs a script.\n * This spec can be used to create a complete workload or an init container.\n *\n * @param options The options to create the container spec.\n * @returns The container spec.\n */\nexport function createScriptContainer(options: ScriptContainer): Output<Container> {\n const bundle = output(options.bundle)\n\n return output({\n options,\n image: bundle.image,\n volumeMounts: bundle.volumeMounts,\n volumes: bundle.volumes,\n environment: bundle.environment,\n allowedEndpoints: bundle.allowedEndpoints,\n }).apply(({ options, image, volumeMounts, volumes, environment, allowedEndpoints }) => {\n return {\n image,\n command: [\"/scripts/entrypoint.sh\", `/scripts/${options.main}`],\n\n ...options,\n\n volumeMounts: [...volumeMounts, ...(options.volumeMounts ?? [])],\n volumes: [...volumes, ...(options.volumes ?? [])],\n environment: merge(environment, options.environment),\n allowedEndpoints: [...allowedEndpoints, ...(options.allowedEndpoints ?? [])],\n } as Container\n })\n}\n","import { batch, type types } from \"@pulumi/kubernetes\"\nimport { ComponentResource, Output, output, type ComponentResourceOptions } from \"@highstate/pulumi\"\nimport { mergeDeep, omit } from \"remeda\"\nimport { commonExtraArgs, getProvider, mapMetadata } from \"./shared\"\nimport { getWorkloadComponents, type WorkloadArgs } from \"./workload\"\n\nexport type JobArgs = WorkloadArgs &\n Omit<Partial<types.input.batch.v1.JobSpec>, \"template\"> & {\n template?: {\n metadata?: types.input.meta.v1.ObjectMeta\n spec?: Partial<types.input.core.v1.PodSpec>\n }\n }\n\nconst jobExtraArgs = [...commonExtraArgs, \"container\", \"containers\"] as const\n\nexport class Job extends ComponentResource {\n /**\n * The underlying Kubernetes job.\n */\n public readonly job: Output<batch.v1.Job>\n\n constructor(name: string, args: JobArgs, opts: ComponentResourceOptions) {\n super(\"highstate:k8s:Job\", name, args, opts)\n\n const { podTemplate } = getWorkloadComponents(name, args, () => this, opts)\n\n this.job = output({ args, podTemplate }).apply(async ({ args, podTemplate }) => {\n return new batch.v1.Job(\n name,\n {\n metadata: mapMetadata(args, name),\n spec: mergeDeep(\n {\n template: mergeDeep(\n {\n spec: {\n restartPolicy: \"Never\",\n },\n },\n podTemplate,\n ),\n } satisfies types.input.batch.v1.JobSpec,\n omit(args, jobExtraArgs) as types.input.batch.v1.JobSpec,\n ),\n },\n {\n ...opts,\n parent: this,\n provider: await getProvider(args.cluster),\n },\n )\n })\n }\n}\n","import type { RequiredKeys } from \"@highstate/contract\"\nimport { batch, type types } from \"@pulumi/kubernetes\"\nimport { ComponentResource, Output, output, type ComponentResourceOptions } from \"@highstate/pulumi\"\nimport { mergeDeep, omit } from \"remeda\"\nimport { commonExtraArgs, getProvider, mapMetadata } from \"./shared\"\nimport { getWorkloadComponents, type WorkloadArgs } from \"./workload\"\n\nexport type CronJobArgs = WorkloadArgs &\n Omit<RequiredKeys<Partial<types.input.batch.v1.CronJobSpec>, \"schedule\">, \"jobTemplate\"> & {\n jobTemplate?: {\n metadata?: types.input.meta.v1.ObjectMeta\n spec?: Omit<types.input.batch.v1.JobSpec, \"template\"> & {\n template?: {\n metadata?: types.input.meta.v1.ObjectMeta\n spec?: Partial<types.input.core.v1.PodSpec>\n }\n }\n }\n }\n\nconst cronJobExtraArgs = [...commonExtraArgs, \"container\", \"containers\"] as const\n\nexport class CronJob extends ComponentResource {\n /**\n * The underlying Kubernetes job.\n */\n public readonly cronJob: Output<batch.v1.CronJob>\n\n constructor(name: string, args: CronJobArgs, opts: ComponentResourceOptions) {\n super(\"highstate:k8s:CronJob\", name, args, opts)\n\n const { podTemplate } = getWorkloadComponents(name, args, () => this, opts)\n\n this.cronJob = output({ args, podTemplate }).apply(async ({ args, podTemplate }) => {\n return new batch.v1.CronJob(\n name,\n {\n metadata: mapMetadata(args, name),\n\n spec: mergeDeep(\n {\n jobTemplate: {\n spec: {\n template: mergeDeep(\n {\n spec: {\n restartPolicy: \"Never\",\n },\n },\n podTemplate,\n ),\n },\n },\n\n schedule: args.schedule,\n } satisfies types.input.batch.v1.CronJobSpec,\n omit(args, cronJobExtraArgs) as types.input.batch.v1.CronJobSpec,\n ),\n },\n {\n ...opts,\n parent: this,\n provider: await getProvider(args.cluster),\n },\n )\n })\n }\n}\n"],"mappings":"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","names":["args","gateway","normalize","output","output","args","normalize","scriptEnvironment","hasFunctionScripts","output","options","ComponentResource","output","ComponentResource","output","args","podTemplate","batch","ComponentResource","output","mergeDeep","omit","ComponentResource","output","args","podTemplate","batch","mergeDeep","omit"]}
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import {
|
2
2
|
Chart
|
3
|
-
} from "../../chunk-
|
4
|
-
import "../../chunk-
|
3
|
+
} from "../../chunk-5S4JPM4M.js";
|
4
|
+
import "../../chunk-WEKIQRCZ.js";
|
5
5
|
import {
|
6
6
|
Namespace
|
7
|
-
} from "../../chunk-
|
7
|
+
} from "../../chunk-Y3LZSX7I.js";
|
8
8
|
|
9
9
|
// src/units/cert-manager/index.ts
|
10
10
|
import { k8s } from "@highstate/library";
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@highstate/k8s",
|
3
|
-
"version": "0.9.
|
3
|
+
"version": "0.9.11",
|
4
4
|
"type": "module",
|
5
5
|
"files": [
|
6
6
|
"dist",
|
@@ -28,12 +28,12 @@
|
|
28
28
|
"generate-crds": "./scripts/generate-crds.sh"
|
29
29
|
},
|
30
30
|
"dependencies": {
|
31
|
-
"@highstate/cert-manager": "^0.9.
|
32
|
-
"@highstate/common": "^0.9.
|
33
|
-
"@highstate/contract": "^0.9.
|
34
|
-
"@highstate/gateway-api": "^0.9.
|
35
|
-
"@highstate/library": "^0.9.
|
36
|
-
"@highstate/pulumi": "^0.9.
|
31
|
+
"@highstate/cert-manager": "^0.9.11",
|
32
|
+
"@highstate/common": "^0.9.11",
|
33
|
+
"@highstate/contract": "^0.9.11",
|
34
|
+
"@highstate/gateway-api": "^0.9.11",
|
35
|
+
"@highstate/library": "^0.9.11",
|
36
|
+
"@highstate/pulumi": "^0.9.11",
|
37
37
|
"@kubernetes/client-node": "^1.1.0",
|
38
38
|
"@pulumi/command": "^1.0.2",
|
39
39
|
"@pulumi/kubernetes": "^4.18.0",
|
@@ -46,7 +46,7 @@
|
|
46
46
|
"remeda": "^2.21.0"
|
47
47
|
},
|
48
48
|
"devDependencies": {
|
49
|
-
"@highstate/cli": "^0.9.
|
49
|
+
"@highstate/cli": "^0.9.11"
|
50
50
|
},
|
51
|
-
"gitHead": "
|
51
|
+
"gitHead": "d62cae30ea9fadaa1aa4fb3b5734a16cf53810ce"
|
52
52
|
}
|
@@ -0,0 +1,180 @@
|
|
1
|
+
import type { k8s } from "@highstate/library"
|
2
|
+
import { core, type types } from "@pulumi/kubernetes"
|
3
|
+
import {
|
4
|
+
ComponentResource,
|
5
|
+
output,
|
6
|
+
Output,
|
7
|
+
type ComponentResourceOptions,
|
8
|
+
type Input,
|
9
|
+
type Inputs,
|
10
|
+
} from "@pulumi/pulumi"
|
11
|
+
import { getProvider, mapMetadata, withPatchName, type CommonArgs } from "./shared"
|
12
|
+
|
13
|
+
export type ConfigMapArgs = CommonArgs &
|
14
|
+
Omit<types.input.core.v1.ConfigMap, "kind" | "metadata" | "apiVersion">
|
15
|
+
|
16
|
+
export type CreateOrPatchConfigMapArgs = ConfigMapArgs & {
|
17
|
+
/**
|
18
|
+
* The resource to use to determine the name of the config map.
|
19
|
+
*
|
20
|
+
* If not provided, the config map will be created, otherwise it will be retrieved/patched.
|
21
|
+
*/
|
22
|
+
existing: Input<k8s.Resource> | undefined
|
23
|
+
}
|
24
|
+
|
25
|
+
export abstract class ConfigMap extends ComponentResource {
|
26
|
+
protected constructor(
|
27
|
+
type: string,
|
28
|
+
name: string,
|
29
|
+
args: Inputs,
|
30
|
+
opts: ComponentResourceOptions | undefined,
|
31
|
+
|
32
|
+
/**
|
33
|
+
* The cluster where the config map is created.
|
34
|
+
*/
|
35
|
+
readonly cluster: Output<k8s.Cluster>,
|
36
|
+
|
37
|
+
/**
|
38
|
+
* The metadata of the underlying Kubernetes config map.
|
39
|
+
*/
|
40
|
+
readonly metadata: Output<types.output.meta.v1.ObjectMeta>,
|
41
|
+
|
42
|
+
/**
|
43
|
+
* The data of the underlying Kubernetes config map.
|
44
|
+
*/
|
45
|
+
readonly data: Output<Record<string, string>>,
|
46
|
+
) {
|
47
|
+
super(type, name, args, opts)
|
48
|
+
}
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Creates a new config map.
|
52
|
+
*/
|
53
|
+
static create(name: string, args: ConfigMapArgs, opts?: ComponentResourceOptions): ConfigMap {
|
54
|
+
return new CreatedConfigMap(name, args, opts)
|
55
|
+
}
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Creates a new config map or patches an existing one.
|
59
|
+
*
|
60
|
+
* Will throw an error if the config map does not exist when `args.resource` is provided.
|
61
|
+
*/
|
62
|
+
static createOrPatch(
|
63
|
+
name: string,
|
64
|
+
args: CreateOrPatchConfigMapArgs,
|
65
|
+
opts?: ComponentResourceOptions,
|
66
|
+
): ConfigMap {
|
67
|
+
if (!args.existing) {
|
68
|
+
return new CreatedConfigMap(name, args, opts)
|
69
|
+
}
|
70
|
+
|
71
|
+
return new ConfigMapPatch(
|
72
|
+
name,
|
73
|
+
{
|
74
|
+
...args,
|
75
|
+
name: withPatchName("configmap", args.existing, args.cluster),
|
76
|
+
namespace: output(args.existing).metadata.namespace,
|
77
|
+
},
|
78
|
+
opts,
|
79
|
+
)
|
80
|
+
}
|
81
|
+
|
82
|
+
/**
|
83
|
+
* Gets an existing config map.
|
84
|
+
*
|
85
|
+
* Will throw an error if the config map does not exist.
|
86
|
+
*/
|
87
|
+
static get(
|
88
|
+
name: string,
|
89
|
+
id: Input<string>,
|
90
|
+
cluster: Input<k8s.Cluster>,
|
91
|
+
opts?: ComponentResourceOptions,
|
92
|
+
): ConfigMap {
|
93
|
+
return new ExternalConfigMap(name, id, cluster, opts)
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
class CreatedConfigMap extends ConfigMap {
|
98
|
+
constructor(name: string, args: ConfigMapArgs, opts?: ComponentResourceOptions) {
|
99
|
+
const configMap = output(args).apply(async args => {
|
100
|
+
return new core.v1.ConfigMap(
|
101
|
+
name,
|
102
|
+
{
|
103
|
+
metadata: mapMetadata(args, name),
|
104
|
+
data: args.data,
|
105
|
+
},
|
106
|
+
{
|
107
|
+
...opts,
|
108
|
+
parent: this,
|
109
|
+
provider: await getProvider(args.cluster),
|
110
|
+
},
|
111
|
+
)
|
112
|
+
})
|
113
|
+
|
114
|
+
super(
|
115
|
+
"highstate:k8s:ConfigMap",
|
116
|
+
name,
|
117
|
+
args,
|
118
|
+
opts,
|
119
|
+
output(args.cluster),
|
120
|
+
configMap.metadata,
|
121
|
+
configMap.data,
|
122
|
+
)
|
123
|
+
}
|
124
|
+
}
|
125
|
+
|
126
|
+
class ConfigMapPatch extends ConfigMap {
|
127
|
+
constructor(name: string, args: ConfigMapArgs, opts?: ComponentResourceOptions) {
|
128
|
+
const configMap = output(args).apply(async args => {
|
129
|
+
return new core.v1.ConfigMapPatch(
|
130
|
+
name,
|
131
|
+
{
|
132
|
+
metadata: mapMetadata(args, name),
|
133
|
+
data: args.data,
|
134
|
+
},
|
135
|
+
{
|
136
|
+
...opts,
|
137
|
+
parent: this,
|
138
|
+
provider: await getProvider(args.cluster),
|
139
|
+
},
|
140
|
+
)
|
141
|
+
})
|
142
|
+
|
143
|
+
super(
|
144
|
+
"highstate:k8s:ConfigMapPatch",
|
145
|
+
name,
|
146
|
+
args,
|
147
|
+
opts,
|
148
|
+
output(args.cluster),
|
149
|
+
configMap.metadata,
|
150
|
+
configMap.data,
|
151
|
+
)
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
class ExternalConfigMap extends ConfigMap {
|
156
|
+
constructor(
|
157
|
+
name: string,
|
158
|
+
id: Input<string>,
|
159
|
+
cluster: Input<k8s.Cluster>,
|
160
|
+
opts?: ComponentResourceOptions,
|
161
|
+
) {
|
162
|
+
const configMap = output(id).apply(async realName => {
|
163
|
+
return core.v1.ConfigMap.get(name, realName, {
|
164
|
+
...opts,
|
165
|
+
parent: this,
|
166
|
+
provider: await getProvider(cluster),
|
167
|
+
})
|
168
|
+
})
|
169
|
+
|
170
|
+
super(
|
171
|
+
"highstate:k8s:ExternalConfigMap",
|
172
|
+
name,
|
173
|
+
{ id, cluster },
|
174
|
+
opts,
|
175
|
+
output(cluster),
|
176
|
+
configMap.metadata,
|
177
|
+
configMap.data,
|
178
|
+
)
|
179
|
+
}
|
180
|
+
}
|
package/src/container.ts
CHANGED
@@ -1,10 +1,18 @@
|
|
1
1
|
import type { PartialKeys } from "@highstate/contract"
|
2
2
|
import type { k8s, network } from "@highstate/library"
|
3
3
|
import { core, type types } from "@pulumi/kubernetes"
|
4
|
-
import {
|
4
|
+
import {
|
5
|
+
normalize,
|
6
|
+
Output,
|
7
|
+
output,
|
8
|
+
type Input,
|
9
|
+
type InputArray,
|
10
|
+
type Unwrap,
|
11
|
+
} from "@highstate/pulumi"
|
5
12
|
import { concat, map, omit } from "remeda"
|
6
13
|
import { PersistentVolumeClaim } from "./pvc"
|
7
14
|
import { Secret } from "./secret"
|
15
|
+
import { ConfigMap } from "./config-map"
|
8
16
|
|
9
17
|
export type Container = Omit<PartialKeys<types.input.core.v1.Container, "name">, "volumeMounts"> & {
|
10
18
|
/**
|
@@ -126,9 +134,10 @@ export type WorkloadVolume =
|
|
126
134
|
| types.input.core.v1.Volume
|
127
135
|
| core.v1.PersistentVolumeClaim
|
128
136
|
| PersistentVolumeClaim
|
129
|
-
| Secret
|
130
137
|
| core.v1.ConfigMap
|
138
|
+
| ConfigMap
|
131
139
|
| core.v1.Secret
|
140
|
+
| Secret
|
132
141
|
|
133
142
|
export function mapContainerToRaw(
|
134
143
|
container: Unwrap<Container>,
|
@@ -289,6 +298,15 @@ export function mapWorkloadVolume(volume: WorkloadVolume) {
|
|
289
298
|
}
|
290
299
|
}
|
291
300
|
|
301
|
+
if (volume instanceof ConfigMap) {
|
302
|
+
return {
|
303
|
+
name: volume.metadata.name,
|
304
|
+
configMap: {
|
305
|
+
name: volume.metadata.name,
|
306
|
+
},
|
307
|
+
}
|
308
|
+
}
|
309
|
+
|
292
310
|
if (core.v1.PersistentVolumeClaim.isInstance(volume)) {
|
293
311
|
return {
|
294
312
|
name: volume.metadata.name,
|
@@ -318,3 +336,31 @@ export function mapWorkloadVolume(volume: WorkloadVolume) {
|
|
318
336
|
|
319
337
|
return volume
|
320
338
|
}
|
339
|
+
|
340
|
+
export function getWorkloadVolumeResourceUuid(volume: WorkloadVolume): Output<string | undefined> {
|
341
|
+
if (volume instanceof PersistentVolumeClaim) {
|
342
|
+
return volume.metadata.uid
|
343
|
+
}
|
344
|
+
|
345
|
+
if (volume instanceof Secret) {
|
346
|
+
return volume.metadata.uid
|
347
|
+
}
|
348
|
+
|
349
|
+
if (volume instanceof ConfigMap) {
|
350
|
+
return volume.metadata.uid
|
351
|
+
}
|
352
|
+
|
353
|
+
if (core.v1.PersistentVolumeClaim.isInstance(volume)) {
|
354
|
+
return volume.metadata.uid
|
355
|
+
}
|
356
|
+
|
357
|
+
if (core.v1.ConfigMap.isInstance(volume)) {
|
358
|
+
return volume.metadata.uid
|
359
|
+
}
|
360
|
+
|
361
|
+
if (core.v1.Secret.isInstance(volume)) {
|
362
|
+
return volume.metadata.uid
|
363
|
+
}
|
364
|
+
|
365
|
+
return output(undefined)
|
366
|
+
}
|
package/src/cron-job.ts
CHANGED
package/src/custom.ts
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
import type { k8s } from "@highstate/library"
|
2
|
+
import { dynamic, interpolate, Output, type Input } from "@pulumi/pulumi"
|
3
|
+
import { KubeConfig } from "@kubernetes/client-node"
|
4
|
+
import { toPromise } from "@highstate/pulumi"
|
5
|
+
|
6
|
+
type CustomOperationInputs = {
|
7
|
+
/**
|
8
|
+
* The cluster to perform the operation on.
|
9
|
+
*/
|
10
|
+
cluster: Input<k8s.Cluster>
|
11
|
+
}
|
12
|
+
|
13
|
+
type WorkloadReloadInputs = CustomOperationInputs & {
|
14
|
+
/**
|
15
|
+
* The type of workload to reload.
|
16
|
+
*/
|
17
|
+
workloadType: Input<"Deployment" | "StatefulSet" | "DaemonSet">
|
18
|
+
|
19
|
+
/**
|
20
|
+
* The namespace of the workload to reload.
|
21
|
+
*/
|
22
|
+
namespace: Input<string>
|
23
|
+
|
24
|
+
/**
|
25
|
+
* The name of the workload to reload.
|
26
|
+
*/
|
27
|
+
name: Input<string>
|
28
|
+
}
|
29
|
+
|
30
|
+
abstract class CustomOperation<
|
31
|
+
TInputs extends CustomOperationInputs = CustomOperationInputs,
|
32
|
+
TOutputs extends CustomOperationInputs = CustomOperationInputs,
|
33
|
+
>
|
34
|
+
extends dynamic.Resource
|
35
|
+
implements dynamic.ResourceProvider<TInputs, TOutputs>
|
36
|
+
{
|
37
|
+
async create(inputs: TInputs): Promise<dynamic.CreateResult<TOutputs>> {
|
38
|
+
const kubeconfig = await this.createKubeConfig(inputs.cluster)
|
39
|
+
|
40
|
+
return {
|
41
|
+
id: this.getId(inputs),
|
42
|
+
outs: await this.onCreate(kubeconfig, inputs),
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
async update(
|
47
|
+
_id: string,
|
48
|
+
outputs: TOutputs,
|
49
|
+
inputs: TInputs,
|
50
|
+
): Promise<dynamic.UpdateResult<TOutputs>> {
|
51
|
+
const kubeconfig = await this.createKubeConfig(inputs.cluster)
|
52
|
+
|
53
|
+
if (this.onUpdate) {
|
54
|
+
return {
|
55
|
+
outs: await this.onUpdate(kubeconfig, inputs, outputs),
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
return { outs: outputs }
|
60
|
+
}
|
61
|
+
|
62
|
+
async delete(_id: string, outputs: TOutputs): Promise<void> {
|
63
|
+
const kubeconfig = await this.createKubeConfig(outputs.cluster)
|
64
|
+
|
65
|
+
if (this.onDelete) {
|
66
|
+
await this.onDelete(kubeconfig, outputs)
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
private async createKubeConfig(cluster: Input<k8s.Cluster>): Promise<KubeConfig> {
|
71
|
+
const clusterConfig = await toPromise(cluster)
|
72
|
+
|
73
|
+
const kubeconfig = new KubeConfig()
|
74
|
+
kubeconfig.loadFromString(clusterConfig.kubeconfig)
|
75
|
+
|
76
|
+
return kubeconfig
|
77
|
+
}
|
78
|
+
|
79
|
+
abstract getId(inputs: TInputs): Input<string>
|
80
|
+
|
81
|
+
abstract onCreate(kubeconfig: KubeConfig, inputs: TInputs): Promise<Omit<TOutputs, "cluster">>
|
82
|
+
|
83
|
+
onUpdate?(
|
84
|
+
kubeconfig: KubeConfig,
|
85
|
+
inputs: TInputs,
|
86
|
+
outputs: TOutputs,
|
87
|
+
): Promise<Omit<TOutputs, "cluster">>
|
88
|
+
|
89
|
+
onDelete?(kubeconfig: KubeConfig, outputs: TOutputs): Promise<void>
|
90
|
+
}
|
91
|
+
|
92
|
+
export class WorkloadReload extends CustomOperation<WorkloadReloadInputs> {
|
93
|
+
override getId(inputs: WorkloadReloadInputs): Output<string> {
|
94
|
+
return interpolate`highstate:k8s:workload-reload:${inputs.workloadType}:${inputs.namespace}:${inputs.name}`
|
95
|
+
}
|
96
|
+
|
97
|
+
override async onCreate(kubeconfig: KubeConfig, inputs: WorkloadReloadInputs) {
|
98
|
+
const apps = kubeconfig.makeApiClient(SchedulingV1Api)
|
99
|
+
|
100
|
+
apps.res
|
101
|
+
|
102
|
+
return {}
|
103
|
+
}
|
104
|
+
}
|
package/src/helm.ts
CHANGED
@@ -56,7 +56,7 @@ export type ChartArgs = Omit<
|
|
56
56
|
/**
|
57
57
|
* The http route args to bind the service to.
|
58
58
|
*/
|
59
|
-
httpRoute?: Input<HttpRouteArgs
|
59
|
+
httpRoute?: Input<Omit<HttpRouteArgs, "cluster">>
|
60
60
|
|
61
61
|
/**
|
62
62
|
* The network policy to apply to the chart.
|
@@ -158,6 +158,7 @@ export class Chart extends ComponentResource {
|
|
158
158
|
name,
|
159
159
|
{
|
160
160
|
...httpRoute,
|
161
|
+
cluster: args.cluster,
|
161
162
|
rule: {
|
162
163
|
backend: this.service,
|
163
164
|
},
|