@highstate/k8s 0.7.2 → 0.7.4
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/{helm-wPTgVV1N.js → chunk-K4WKJ4L5.js} +89 -47
- package/dist/chunk-K4WKJ4L5.js.map +1 -0
- package/dist/{shared-Clzbl5K-.js → chunk-T5Z2M4JE.js} +21 -7
- package/dist/chunk-T5Z2M4JE.js.map +1 -0
- package/dist/highstate.manifest.json +9 -0
- package/dist/index.js +304 -154
- package/dist/index.js.map +1 -0
- package/dist/units/access-point/index.js +9 -7
- package/dist/units/access-point/index.js.map +1 -0
- package/dist/units/cert-manager/index.js +29 -29
- package/dist/units/cert-manager/index.js.map +1 -0
- package/dist/units/dns01-issuer/index.js +22 -14
- package/dist/units/dns01-issuer/index.js.map +1 -0
- package/dist/units/existing-cluster/index.js +49 -21
- package/dist/units/existing-cluster/index.js.map +1 -0
- package/package.json +15 -16
- package/src/access-point.ts +185 -0
- package/src/container.ts +271 -0
- package/src/cron-job.ts +77 -0
- package/src/deployment.ts +210 -0
- package/src/gateway/backend.ts +61 -0
- package/src/gateway/http-route.ts +139 -0
- package/src/gateway/index.ts +2 -0
- package/src/helm.ts +298 -0
- package/src/index.ts +61 -0
- package/src/job.ts +66 -0
- package/src/network-policy.ts +732 -0
- package/src/pod.ts +5 -0
- package/src/pvc.ts +178 -0
- package/src/scripting/bundle.ts +244 -0
- package/src/scripting/container.ts +44 -0
- package/src/scripting/environment.ts +79 -0
- package/src/scripting/index.ts +3 -0
- package/src/service.ts +279 -0
- package/src/shared.ts +150 -0
- package/src/stateful-set.ts +159 -0
- package/src/units/access-point/index.ts +12 -0
- package/src/units/cert-manager/index.ts +37 -0
- package/src/units/dns01-issuer/index.ts +41 -0
- package/src/units/dns01-issuer/solver.ts +23 -0
- package/src/units/existing-cluster/index.ts +107 -0
- package/src/workload.ts +150 -0
- package/assets/charts.json +0 -8
- package/dist/index.d.ts +0 -1036
@@ -0,0 +1,107 @@
|
|
1
|
+
import { text } from "@highstate/contract"
|
2
|
+
import { k8s } from "@highstate/library"
|
3
|
+
import { forUnit, getUnitInstanceName, secret, toPromise } from "@highstate/pulumi"
|
4
|
+
import { core, Provider } from "@pulumi/kubernetes"
|
5
|
+
import { KubeConfig, AppsV1Api, CoreV1Api } from "@kubernetes/client-node"
|
6
|
+
|
7
|
+
const { name, args, secrets, outputs } = forUnit(k8s.existingCluster)
|
8
|
+
|
9
|
+
const kubeconfigContent = await toPromise(secrets.kubeconfig.apply(JSON.stringify))
|
10
|
+
|
11
|
+
const provider = new Provider(name, { kubeconfig: kubeconfigContent })
|
12
|
+
|
13
|
+
let cni: string | undefined
|
14
|
+
|
15
|
+
const kubeConfig = new KubeConfig()
|
16
|
+
kubeConfig.loadFromString(kubeconfigContent)
|
17
|
+
|
18
|
+
const appsApi = kubeConfig.makeApiClient(AppsV1Api)
|
19
|
+
const nodeApi = kubeConfig.makeApiClient(CoreV1Api)
|
20
|
+
|
21
|
+
const isCilium = await appsApi
|
22
|
+
.readNamespacedDaemonSet({ name: "cilium", namespace: "kube-system" })
|
23
|
+
.then(() => true)
|
24
|
+
.catch(() => false)
|
25
|
+
|
26
|
+
if (isCilium) {
|
27
|
+
cni = "cilium"
|
28
|
+
}
|
29
|
+
|
30
|
+
const isPrivateIp = (ip: string) => {
|
31
|
+
const privateIpRegex = /^(10|172\.16|192\.168)\./
|
32
|
+
return privateIpRegex.test(ip)
|
33
|
+
}
|
34
|
+
|
35
|
+
const nodes = await nodeApi.listNode()
|
36
|
+
|
37
|
+
const externalIps = nodes.items.flatMap(node => {
|
38
|
+
const addresses = node.status?.addresses ?? []
|
39
|
+
const externalIp = addresses.find(address => address.type === "ExternalIP")
|
40
|
+
const internalIp = addresses.find(address => address.type === "InternalIP")
|
41
|
+
|
42
|
+
const result: string[] = []
|
43
|
+
|
44
|
+
if (externalIp?.address) {
|
45
|
+
result.push(externalIp.address)
|
46
|
+
}
|
47
|
+
|
48
|
+
if (internalIp?.address && args.internalIpsPolicy === "always") {
|
49
|
+
result.push(internalIp.address)
|
50
|
+
}
|
51
|
+
|
52
|
+
if (
|
53
|
+
internalIp?.address &&
|
54
|
+
args.internalIpsPolicy === "public" &&
|
55
|
+
!isPrivateIp(internalIp.address)
|
56
|
+
) {
|
57
|
+
result.push(internalIp.address)
|
58
|
+
}
|
59
|
+
|
60
|
+
return result
|
61
|
+
})
|
62
|
+
|
63
|
+
const kubeSystem = core.v1.Namespace.get("kube-system", "kube-system", { provider })
|
64
|
+
|
65
|
+
export default outputs({
|
66
|
+
cluster: {
|
67
|
+
info: {
|
68
|
+
id: kubeSystem.metadata.uid,
|
69
|
+
name,
|
70
|
+
cni,
|
71
|
+
externalIps,
|
72
|
+
},
|
73
|
+
kubeconfig: secret(kubeconfigContent),
|
74
|
+
},
|
75
|
+
|
76
|
+
$terminals: {
|
77
|
+
management: {
|
78
|
+
title: `K8S: ${getUnitInstanceName()}`,
|
79
|
+
description: "Manage the cluster using kubectl and helm",
|
80
|
+
image: "ghcr.io/exeteres/highstate/terminal-kubectl",
|
81
|
+
command: ["bash", "/welcome.sh"],
|
82
|
+
files: {
|
83
|
+
"/kubeconfig": secret(kubeconfigContent),
|
84
|
+
|
85
|
+
"/welcome.sh": text`
|
86
|
+
if [ "$HIGHSTATE_TERMINAL_FIRST_LAUNCH" = "1" ]; then
|
87
|
+
echo "Connecting to the cluster..."
|
88
|
+
kubectl cluster-info
|
89
|
+
|
90
|
+
echo "Use 'kubectl' and 'helm' to manage the cluster."
|
91
|
+
echo
|
92
|
+
fi
|
93
|
+
|
94
|
+
exec script -q -c bash /dev/null
|
95
|
+
`,
|
96
|
+
},
|
97
|
+
env: {
|
98
|
+
KUBECONFIG: "/kubeconfig",
|
99
|
+
},
|
100
|
+
},
|
101
|
+
},
|
102
|
+
|
103
|
+
$status: {
|
104
|
+
clusterId: kubeSystem.metadata.uid,
|
105
|
+
cni: cni ?? "unknown",
|
106
|
+
},
|
107
|
+
})
|
package/src/workload.ts
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
import type { types } from "@pulumi/kubernetes"
|
2
|
+
import type { k8s } from "@highstate/library"
|
3
|
+
import { normalize, type ComponentResourceOptions, type InputArray } from "@highstate/pulumi"
|
4
|
+
import { ComponentResource, output, type Input, type Output } from "@pulumi/pulumi"
|
5
|
+
import { uniqueBy } from "remeda"
|
6
|
+
import { commonExtraArgs, type CommonArgs } from "./shared"
|
7
|
+
import { mapContainerPortToServicePort, Service, type ServiceArgs } from "./service"
|
8
|
+
import { HttpRoute, type HttpRouteArgs } from "./gateway"
|
9
|
+
import {
|
10
|
+
mapContainerToRaw,
|
11
|
+
mapWorkloadVolume,
|
12
|
+
type Container,
|
13
|
+
type WorkloadVolume,
|
14
|
+
} from "./container"
|
15
|
+
|
16
|
+
export type WorkloadArgs = CommonArgs & {
|
17
|
+
container?: Input<Container>
|
18
|
+
containers?: InputArray<Container>
|
19
|
+
|
20
|
+
/**
|
21
|
+
* The cluster to create the resource in.
|
22
|
+
*/
|
23
|
+
cluster: Input<k8s.Cluster>
|
24
|
+
}
|
25
|
+
|
26
|
+
export const workloadExtraArgs = [...commonExtraArgs, "container", "containers"] as const
|
27
|
+
|
28
|
+
export class WorkloadBase<TArgs extends WorkloadArgs> extends ComponentResource {
|
29
|
+
protected readonly containers: Output<types.input.core.v1.Container[]>
|
30
|
+
protected readonly volumes: Output<types.input.core.v1.Volume[]>
|
31
|
+
|
32
|
+
constructor(type: string, name: string, args: TArgs, opts: ComponentResourceOptions | undefined) {
|
33
|
+
super(type, name, args, opts)
|
34
|
+
|
35
|
+
const containers = output(args).apply(args => normalize(args.container, args.containers))
|
36
|
+
|
37
|
+
this.containers = containers.apply(containers =>
|
38
|
+
containers.map(container => mapContainerToRaw(container, name)),
|
39
|
+
)
|
40
|
+
|
41
|
+
this.volumes = containers.apply(containers =>
|
42
|
+
containers
|
43
|
+
.flatMap(container => normalize(container.volume, container.volumes))
|
44
|
+
.map(mapWorkloadVolume),
|
45
|
+
)
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
export type PublicWorkloadArgs = WorkloadArgs & {
|
50
|
+
service?: Input<Omit<ServiceArgs, "cluster" | "namespace">>
|
51
|
+
httpRoute?: Input<Omit<HttpRouteArgs, "cluster" | "namespace">>
|
52
|
+
patch?: Input<k8s.Deployment | k8s.StatefulSet>
|
53
|
+
} & Partial<types.input.apps.v1.StatefulSetSpec>
|
54
|
+
|
55
|
+
export const publicWorkloadExtraArgs = [...workloadExtraArgs, "service", "httpRoute"] as const
|
56
|
+
|
57
|
+
export function getWorkloadComponents(name: string, args: WorkloadArgs) {
|
58
|
+
const labels = {
|
59
|
+
"app.kubernetes.io/name": name,
|
60
|
+
}
|
61
|
+
|
62
|
+
const containers = output(args).apply(args => normalize(args.container, args.containers))
|
63
|
+
const volumes = containers.apply(containers => {
|
64
|
+
const containerVolumes = containers
|
65
|
+
.flatMap(container => normalize(container.volume, container.volumes))
|
66
|
+
.map(mapWorkloadVolume)
|
67
|
+
|
68
|
+
const containerVolumeMounts = containers
|
69
|
+
.flatMap(container => {
|
70
|
+
return normalize(container.volumeMount, container.volumeMounts)
|
71
|
+
.map(volumeMount => {
|
72
|
+
return "volume" in volumeMount ? volumeMount.volume : undefined
|
73
|
+
})
|
74
|
+
.filter(Boolean) as WorkloadVolume[]
|
75
|
+
})
|
76
|
+
.map(mapWorkloadVolume)
|
77
|
+
|
78
|
+
return uniqueBy([...containerVolumes, ...containerVolumeMounts], volume => volume.name)
|
79
|
+
})
|
80
|
+
|
81
|
+
return { labels, containers, volumes }
|
82
|
+
}
|
83
|
+
|
84
|
+
export function getPublicWorkloadComponents(
|
85
|
+
name: string,
|
86
|
+
args: PublicWorkloadArgs,
|
87
|
+
parent: () => ComponentResource,
|
88
|
+
opts: ComponentResourceOptions,
|
89
|
+
) {
|
90
|
+
const { labels, containers, volumes } = getWorkloadComponents(name, args)
|
91
|
+
|
92
|
+
const service = output({ args, containers }).apply(({ args, containers }) => {
|
93
|
+
if (!args.service && !args.httpRoute) {
|
94
|
+
return undefined
|
95
|
+
}
|
96
|
+
|
97
|
+
if (args.patch?.service) {
|
98
|
+
return Service.of(name, args.patch.service, { parent: parent(), ...opts })
|
99
|
+
}
|
100
|
+
|
101
|
+
if (args.patch) {
|
102
|
+
return undefined
|
103
|
+
}
|
104
|
+
|
105
|
+
const ports = containers.flatMap(container => normalize(container.port, container.ports))
|
106
|
+
|
107
|
+
return Service.create(
|
108
|
+
name,
|
109
|
+
{
|
110
|
+
...args.service,
|
111
|
+
selector: labels,
|
112
|
+
cluster: args.cluster,
|
113
|
+
namespace: args.namespace,
|
114
|
+
|
115
|
+
ports:
|
116
|
+
// allow to completely override the ports
|
117
|
+
!args.service?.port && !args.service?.ports
|
118
|
+
? ports.map(mapContainerPortToServicePort)
|
119
|
+
: args.service?.ports,
|
120
|
+
},
|
121
|
+
{ parent: parent(), ...opts },
|
122
|
+
)
|
123
|
+
})
|
124
|
+
|
125
|
+
const httpRoute = output({
|
126
|
+
args,
|
127
|
+
service,
|
128
|
+
}).apply(({ args, service }) => {
|
129
|
+
if (!args.httpRoute || !service) {
|
130
|
+
return undefined
|
131
|
+
}
|
132
|
+
|
133
|
+
if (args.patch) {
|
134
|
+
return undefined
|
135
|
+
}
|
136
|
+
|
137
|
+
return new HttpRoute(
|
138
|
+
name,
|
139
|
+
{
|
140
|
+
...args.httpRoute,
|
141
|
+
rule: {
|
142
|
+
backend: service,
|
143
|
+
},
|
144
|
+
},
|
145
|
+
{ parent: parent(), ...opts },
|
146
|
+
)
|
147
|
+
})
|
148
|
+
|
149
|
+
return { labels, containers, volumes, service, httpRoute }
|
150
|
+
}
|