@highstate/k8s 0.7.1 → 0.7.3

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.
Files changed (44) hide show
  1. package/dist/{helm-wPTgVV1N.js → chunk-K4WKJ4L5.js} +89 -47
  2. package/dist/chunk-K4WKJ4L5.js.map +1 -0
  3. package/dist/{shared-Clzbl5K-.js → chunk-T5Z2M4JE.js} +21 -7
  4. package/dist/chunk-T5Z2M4JE.js.map +1 -0
  5. package/dist/highstate.manifest.json +9 -0
  6. package/dist/index.js +304 -154
  7. package/dist/index.js.map +1 -0
  8. package/dist/units/access-point/index.js +9 -7
  9. package/dist/units/access-point/index.js.map +1 -0
  10. package/dist/units/cert-manager/index.js +29 -29
  11. package/dist/units/cert-manager/index.js.map +1 -0
  12. package/dist/units/dns01-issuer/index.js +22 -14
  13. package/dist/units/dns01-issuer/index.js.map +1 -0
  14. package/dist/units/existing-cluster/index.js +49 -21
  15. package/dist/units/existing-cluster/index.js.map +1 -0
  16. package/package.json +15 -16
  17. package/src/access-point.ts +185 -0
  18. package/src/container.ts +271 -0
  19. package/src/cron-job.ts +77 -0
  20. package/src/deployment.ts +210 -0
  21. package/src/gateway/backend.ts +61 -0
  22. package/src/gateway/http-route.ts +139 -0
  23. package/src/gateway/index.ts +2 -0
  24. package/src/helm.ts +298 -0
  25. package/src/index.ts +61 -0
  26. package/src/job.ts +66 -0
  27. package/src/network-policy.ts +732 -0
  28. package/src/pod.ts +5 -0
  29. package/src/pvc.ts +178 -0
  30. package/src/scripting/bundle.ts +244 -0
  31. package/src/scripting/container.ts +44 -0
  32. package/src/scripting/environment.ts +79 -0
  33. package/src/scripting/index.ts +3 -0
  34. package/src/service.ts +279 -0
  35. package/src/shared.ts +150 -0
  36. package/src/stateful-set.ts +159 -0
  37. package/src/units/access-point/index.ts +12 -0
  38. package/src/units/cert-manager/index.ts +37 -0
  39. package/src/units/dns01-issuer/index.ts +41 -0
  40. package/src/units/dns01-issuer/solver.ts +23 -0
  41. package/src/units/existing-cluster/index.ts +107 -0
  42. package/src/workload.ts +150 -0
  43. package/assets/charts.json +0 -8
  44. package/dist/index.d.ts +0 -1036
package/src/pod.ts ADDED
@@ -0,0 +1,5 @@
1
+ import type { types } from "@pulumi/kubernetes"
2
+
3
+ export const podSpecDefaults: Partial<types.input.core.v1.PodSpec> = {
4
+ automountServiceAccountToken: false,
5
+ }
package/src/pvc.ts ADDED
@@ -0,0 +1,178 @@
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 CustomResourceOptions,
9
+ type Input,
10
+ type Inputs,
11
+ } from "@highstate/pulumi"
12
+ import { deepmerge } from "deepmerge-ts"
13
+ import { omit } from "remeda"
14
+ import {
15
+ commonExtraArgs,
16
+ mapMetadata,
17
+ resourceIdToString,
18
+ verifyProvider,
19
+ type CommonArgs,
20
+ type ResourceId,
21
+ } from "./shared"
22
+
23
+ export type PersistentVolumeClaimArgs = CommonArgs &
24
+ types.input.core.v1.PersistentVolumeClaimSpec & {
25
+ /**
26
+ * The size of the volume to request.
27
+ *
28
+ * By default, the size is set to "100Mi".
29
+ */
30
+ size?: string
31
+
32
+ /**
33
+ * The cluster to create the resource in.
34
+ */
35
+ cluster: Input<k8s.Cluster>
36
+ }
37
+
38
+ const extraPersistentVolumeClaimArgs = [...commonExtraArgs, "size", "cluster"] as const
39
+
40
+ export abstract class PersistentVolumeClaim extends ComponentResource {
41
+ protected constructor(
42
+ type: string,
43
+ name: string,
44
+ args: Inputs,
45
+ opts: ComponentResourceOptions,
46
+
47
+ /**
48
+ * The cluster info associated with the pvc.
49
+ */
50
+ readonly clusterInfo: Output<k8s.ClusterInfo>,
51
+
52
+ /**
53
+ * The metadata of the underlying Kubernetes pvc.
54
+ */
55
+ readonly metadata: Output<types.output.meta.v1.ObjectMeta>,
56
+
57
+ /**
58
+ * The spec of the underlying Kubernetes pvc.
59
+ */
60
+ readonly spec: Output<types.output.core.v1.PersistentVolumeClaimSpec>,
61
+
62
+ /**
63
+ * The status of the underlying Kubernetes pvc.
64
+ */
65
+ readonly status: Output<types.output.core.v1.PersistentVolumeClaimStatus>,
66
+ ) {
67
+ super(type, name, args, opts)
68
+ }
69
+
70
+ /**
71
+ * The Highstate service entity.
72
+ */
73
+ get entity(): Output<k8s.PersistentVolumeClaim> {
74
+ return output({
75
+ type: "k8s.persistent-volume-claim",
76
+ clusterInfo: this.clusterInfo,
77
+ metadata: this.metadata,
78
+ })
79
+ }
80
+
81
+ static create(
82
+ name: string,
83
+ args: PersistentVolumeClaimArgs,
84
+ opts: ComponentResourceOptions,
85
+ ): PersistentVolumeClaim {
86
+ return new CreatedPersistentVolumeClaim(name, args, opts)
87
+ }
88
+
89
+ static of(
90
+ name: string,
91
+ entity: Input<k8s.PersistentVolumeClaim>,
92
+ opts: ComponentResourceOptions,
93
+ ): PersistentVolumeClaim {
94
+ return new ExternalPersistentVolumeClaim(
95
+ name,
96
+ output(entity).metadata,
97
+ output(entity).clusterInfo,
98
+ opts,
99
+ )
100
+ }
101
+ }
102
+
103
+ export class CreatedPersistentVolumeClaim extends PersistentVolumeClaim {
104
+ constructor(name: string, args: PersistentVolumeClaimArgs, opts: CustomResourceOptions) {
105
+ const pvc = output(args).apply(args => {
106
+ return new core.v1.PersistentVolumeClaim(
107
+ name,
108
+ {
109
+ metadata: mapMetadata(args, name),
110
+ spec: deepmerge(
111
+ {
112
+ accessModes: ["ReadWriteOnce"],
113
+ resources: {
114
+ requests: {
115
+ storage: args.size ?? "100Mi",
116
+ },
117
+ },
118
+ } satisfies types.input.core.v1.PersistentVolumeClaimSpec,
119
+ omit(args, extraPersistentVolumeClaimArgs),
120
+ ),
121
+ },
122
+ opts,
123
+ )
124
+ })
125
+
126
+ super(
127
+ "k8s:PersistentVolumeClaim",
128
+ name,
129
+ args,
130
+ opts,
131
+
132
+ output(args.cluster).info,
133
+ pvc.metadata,
134
+ pvc.spec,
135
+ pvc.status,
136
+ )
137
+ }
138
+ }
139
+
140
+ export class ExternalPersistentVolumeClaim extends PersistentVolumeClaim {
141
+ constructor(
142
+ name: string,
143
+ id: Input<ResourceId>,
144
+ clusterInfo: Input<k8s.ClusterInfo>,
145
+ opts: ComponentResourceOptions,
146
+ ) {
147
+ const pvc = output(id).apply(async id => {
148
+ await verifyProvider(opts.provider, this.clusterInfo)
149
+
150
+ return core.v1.PersistentVolumeClaim.get(
151
+ //
152
+ name,
153
+ resourceIdToString(id),
154
+ { parent: this, provider: opts.provider },
155
+ )
156
+ })
157
+
158
+ super(
159
+ "highstate:k8s:ExternalPersistentVolumeClaim",
160
+ name,
161
+ { id, clusterInfo },
162
+ opts,
163
+
164
+ output(clusterInfo),
165
+ pvc.metadata,
166
+ pvc.spec,
167
+ pvc.status,
168
+ )
169
+ }
170
+ }
171
+
172
+ export function getAutoVolumeName(workloadName: string, index: number): string {
173
+ if (index === 0) {
174
+ return `${workloadName}-data`
175
+ }
176
+
177
+ return `${workloadName}-data-${index}`
178
+ }
@@ -0,0 +1,244 @@
1
+ import type { ContainerEnvironment, ContainerVolumeMount, WorkloadVolume } from "../container"
2
+ import { core } from "@pulumi/kubernetes"
3
+ import { apply, normalize, type InputArray } from "@highstate/pulumi"
4
+ import {
5
+ ComponentResource,
6
+ output,
7
+ type ComponentResourceOptions,
8
+ type Input,
9
+ type Output,
10
+ type Unwrap,
11
+ } from "@pulumi/pulumi"
12
+ import { pipe } from "remeda"
13
+ import { deepmerge } from "deepmerge-ts"
14
+ import { text, trimIndentation } from "@highstate/contract"
15
+ import { mapMetadata, type CommonArgs } from "../shared"
16
+ import {
17
+ emptyScriptEnvironment,
18
+ type ResolvedScriptEnvironment,
19
+ type ScriptDistribution,
20
+ type ScriptEnvironment,
21
+ } from "./environment"
22
+
23
+ export type ScriptBundleArgs = CommonArgs & {
24
+ /**
25
+ * The environment to bundle the scripts from.
26
+ */
27
+ environment?: Input<ScriptEnvironment>
28
+
29
+ /**
30
+ * The environments to bundle the scripts from.
31
+ */
32
+ environments?: InputArray<ScriptEnvironment>
33
+
34
+ /**
35
+ * The distribution to use for the scripts.
36
+ */
37
+ distribution: ScriptDistribution
38
+ }
39
+
40
+ export class ScriptBundle extends ComponentResource {
41
+ /**
42
+ * The config map containing the scripts.
43
+ */
44
+ readonly configMap: Output<core.v1.ConfigMap>
45
+
46
+ /**
47
+ * The volumes that should be included in the workload.
48
+ */
49
+ readonly volumes: Output<WorkloadVolume[]>
50
+
51
+ /**
52
+ * The volume mounts that should be defined in the container.
53
+ */
54
+ readonly volumeMounts: Output<ContainerVolumeMount[]>
55
+
56
+ /**
57
+ * The environment variables that should be defined in the container.
58
+ */
59
+ readonly environment: Output<ContainerEnvironment>
60
+
61
+ /**
62
+ * The distribution to use for the scripts.
63
+ */
64
+ readonly distribution: ScriptDistribution
65
+
66
+ constructor(name: string, args: ScriptBundleArgs, opts?: ComponentResourceOptions) {
67
+ super("highstate:k8s:ScriptBundle", name, args, opts)
68
+
69
+ const scriptEnvironment = pipe(
70
+ output(args),
71
+ apply(args => normalize(args.environment, args.environments)),
72
+ apply(args => deepmerge(emptyScriptEnvironment, ...args)),
73
+ ) as Output<Unwrap<ResolvedScriptEnvironment>>
74
+
75
+ this.distribution = args.distribution
76
+ this.environment = scriptEnvironment.environment
77
+
78
+ this.configMap = output({ scriptEnvironment, args }).apply(({ scriptEnvironment, args }) => {
79
+ return new core.v1.ConfigMap(
80
+ name,
81
+ {
82
+ metadata: mapMetadata(args, name),
83
+ data: createScriptData(this.distribution, scriptEnvironment),
84
+ },
85
+ { ...opts, parent: this },
86
+ )
87
+ })
88
+
89
+ this.volumes = scriptEnvironment.volumes.apply(volumes => {
90
+ return [
91
+ ...volumes,
92
+ {
93
+ name: this.configMap.metadata.name,
94
+
95
+ configMap: {
96
+ name: this.configMap.metadata.name,
97
+ defaultMode: 0o550, // read and execute permissions
98
+ },
99
+ },
100
+ ]
101
+ })
102
+
103
+ this.volumeMounts = scriptEnvironment.volumeMounts.apply(volumeMounts => {
104
+ return [
105
+ ...volumeMounts,
106
+ {
107
+ volume: this.configMap,
108
+ mountPath: "/scripts",
109
+ },
110
+ ]
111
+ })
112
+
113
+ this.registerOutputs({
114
+ configMap: this.configMap,
115
+ volumes: this.volumes,
116
+ volumeMounts: this.volumeMounts,
117
+ environment: this.environment,
118
+ })
119
+ }
120
+ }
121
+
122
+ function createScriptData(
123
+ distribution: ScriptDistribution,
124
+ environment: Unwrap<ResolvedScriptEnvironment>,
125
+ ): Record<string, string> {
126
+ const scriptData: Record<string, string> = {}
127
+ const actions: string[] = []
128
+
129
+ const distributionEnvironment = environment[distribution]
130
+
131
+ if (distributionEnvironment.preInstallPackages.length > 0) {
132
+ scriptData["pre-install-packages.sh"] = getInstallPackagesScript(
133
+ distribution,
134
+ distributionEnvironment.preInstallPackages,
135
+ )
136
+
137
+ actions.push(`
138
+ echo "+ Installing pre-install packages..."
139
+ /scripts/pre-install-packages.sh
140
+ echo "+ Pre-install packages installed successfully"
141
+ `)
142
+ }
143
+
144
+ if (Object.keys(distributionEnvironment.preInstallScripts).length > 0) {
145
+ for (const key in distributionEnvironment.preInstallScripts) {
146
+ scriptData[`pre-install-${key}`] = distributionEnvironment.preInstallScripts[key]
147
+
148
+ actions.push(`
149
+ echo "+ Running pre-install script '${key}'..."
150
+ /scripts/pre-install-${key}
151
+ echo "+ Pre-install script '${key}'... Done"
152
+ `)
153
+ }
154
+ }
155
+
156
+ if (distributionEnvironment.packages.length > 0) {
157
+ scriptData["install-packages.sh"] = getInstallPackagesScript(
158
+ distribution,
159
+ distributionEnvironment.packages,
160
+ )
161
+
162
+ actions.push(`
163
+ echo "+ Installing packages..."
164
+ /scripts/install-packages.sh
165
+ echo "+ Packages installed successfully"
166
+ `)
167
+ }
168
+
169
+ if (Object.keys(environment.setupScripts).length > 0) {
170
+ for (const key in environment.setupScripts) {
171
+ scriptData[`setup-${key}`] = environment.setupScripts[key]
172
+
173
+ actions.push(`
174
+ echo "+ Running setup script '${key}'..."
175
+ /scripts/setup-${key}
176
+ echo "+ Setup script '${key}'... Done"
177
+ `)
178
+ }
179
+ }
180
+
181
+ if (Object.keys(environment.cleanupScripts).length > 0) {
182
+ const cleanupActions: string[] = []
183
+
184
+ for (const key in environment.cleanupScripts) {
185
+ scriptData[`cleanup-${key}`] = environment.cleanupScripts[key]
186
+
187
+ cleanupActions.push(`
188
+ echo "+ Running cleanup script '${key}'..."
189
+ /scripts/cleanup-${key}
190
+ echo "+ Cleanup script '${key}'... Done"
191
+ `)
192
+ }
193
+
194
+ actions.push(`
195
+ function cleanup() {
196
+ ${cleanupActions.map(s => s.trim()).join("\n\n")}
197
+ }
198
+
199
+ trap cleanup EXIT
200
+ trap cleanup SIGTERM
201
+ `)
202
+ }
203
+
204
+ for (const key in environment.scripts) {
205
+ scriptData[key] = environment.scripts[key]
206
+ }
207
+
208
+ scriptData["entrypoint.sh"] = trimIndentation(`
209
+ #!/bin/sh
210
+ set -e
211
+
212
+ if [ -z "$1" ]; then
213
+ echo "Usage: entrypoint.sh <main script> [args...]"
214
+ exit 1
215
+ fi
216
+
217
+ ${actions.map(s => s.trim()).join("\n\n")}
218
+
219
+ echo "+ Running main script..."
220
+ $@
221
+ echo "+ Main script completed"
222
+ `)
223
+
224
+ return scriptData
225
+ }
226
+
227
+ function getInstallPackagesScript(distribution: ScriptDistribution, packages: string[]): string {
228
+ if (distribution === "alpine") {
229
+ return text`
230
+ #!/bin/sh
231
+ set -e
232
+
233
+ apk add --no-cache ${packages.join(" ")}
234
+ `
235
+ } else {
236
+ return text`
237
+ #!/bin/sh
238
+ set -e
239
+
240
+ apt-get update
241
+ apt-get install -y ${packages.join(" ")}
242
+ `
243
+ }
244
+ }
@@ -0,0 +1,44 @@
1
+ import type { Container } from "../container"
2
+ import type { ScriptBundle } from "./bundle"
3
+ import { Output, output, type Input } from "@pulumi/pulumi"
4
+ import { merge } from "remeda"
5
+
6
+ export interface ScriptContainer extends Container {
7
+ /**
8
+ * The script bundle to use.
9
+ */
10
+ bundle: Input<ScriptBundle>
11
+
12
+ /**
13
+ * The name of the main script to run.
14
+ * The script must be available in the bundle.
15
+ */
16
+ main: Input<string>
17
+ }
18
+
19
+ /**
20
+ * Creates a spec for a container that runs a script.
21
+ * This spec can be used to create a complete workload or an init container.
22
+ *
23
+ * @param options The options to create the container spec.
24
+ * @returns The container spec.
25
+ */
26
+ export function createScriptContainer(options: ScriptContainer): Output<Container> {
27
+ return output(options).apply(options => {
28
+ const image =
29
+ options.bundle.distribution === "alpine"
30
+ ? "alpine@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c"
31
+ : "ubuntu@sha256:72297848456d5d37d1262630108ab308d3e9ec7ed1c3286a32fe09856619a782"
32
+
33
+ return {
34
+ image,
35
+ command: ["/scripts/entrypoint.sh", `/scripts/${options.main}`],
36
+
37
+ ...options,
38
+
39
+ volumeMounts: merge(options.bundle.volumeMounts, options.volumeMounts),
40
+ volumes: merge(options.bundle.volumes, options.volumes),
41
+ environment: merge(options.bundle.environment, options.environment),
42
+ }
43
+ })
44
+ }
@@ -0,0 +1,79 @@
1
+ import type { Input, InputArray, InputMap } from "@highstate/pulumi"
2
+ import type { ContainerEnvironment, ContainerVolumeMount, WorkloadVolume } from "../container"
3
+
4
+ export type ScriptDistribution = "alpine" | "ubuntu"
5
+
6
+ export type DistributionEnvironment = {
7
+ /**
8
+ * The utility packages that should be installed before running "preInstallScripts".
9
+ *
10
+ * Useful for installing tools like `curl` to install additional repositories.
11
+ */
12
+ preInstallPackages?: InputArray<string>
13
+
14
+ /**
15
+ * The pre-install scripts that should be run before installing packages.
16
+ * Typically, these scripts are used to install additional repositories.
17
+ */
18
+ preInstallScripts?: InputMap<string>
19
+
20
+ /**
21
+ * The packages that are available in the environment.
22
+ */
23
+ packages?: InputArray<string>
24
+ }
25
+
26
+ export type ScriptEnvironment = {
27
+ [distribution in ScriptDistribution]?: DistributionEnvironment
28
+ } & {
29
+ /**
30
+ * The setup scripts that should be run before the script.
31
+ */
32
+ setupScripts?: InputMap<string>
33
+
34
+ /**
35
+ * The cleanup scripts that should be run after the script.
36
+ */
37
+ cleanupScripts?: InputMap<string>
38
+
39
+ /**
40
+ * The arbitrary scripts available in the environment.
41
+ */
42
+ scripts?: InputMap<string>
43
+
44
+ /**
45
+ * The volumes that should be defined in the environment.
46
+ */
47
+ volumes?: InputArray<WorkloadVolume>
48
+
49
+ /**
50
+ * The volume mounts that should be defined in the environment.
51
+ */
52
+ volumeMounts?: InputArray<ContainerVolumeMount>
53
+
54
+ /**
55
+ * The environment variables that should be defined in the environment.
56
+ */
57
+ environment?: Input<ContainerEnvironment>
58
+ }
59
+
60
+ export type ResolvedScriptEnvironment = Omit<Required<ScriptEnvironment>, ScriptDistribution> & {
61
+ [distribution in ScriptDistribution]: Required<DistributionEnvironment>
62
+ }
63
+
64
+ const emptyDistributionEnvironment: Required<DistributionEnvironment> = {
65
+ preInstallPackages: [],
66
+ preInstallScripts: {},
67
+ packages: [],
68
+ }
69
+
70
+ export const emptyScriptEnvironment: ResolvedScriptEnvironment = {
71
+ alpine: emptyDistributionEnvironment,
72
+ ubuntu: emptyDistributionEnvironment,
73
+ setupScripts: {},
74
+ cleanupScripts: {},
75
+ scripts: {},
76
+ volumes: [],
77
+ volumeMounts: [],
78
+ environment: {},
79
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./bundle"
2
+ export * from "./container"
3
+ export * from "./environment"