Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add podinfo module #113

Merged
merged 1 commit into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions podinfo/config/def.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import regex
import k8s.api.core.v1 as corev1
import k8s.apimachinery.pkg.apis.meta.v1 as metav1

gLabels = {}
gAnnotations = gLabels
some = lambda x: any -> bool {
x not in [None, Undefined]
}
schema Config:
"""
Config defines the schema and defaults for the values.

Attributes
----------
ui: UIConfig
Podinfo optional UI setting.
moduleVersion: str

kubeVersion: str
replicas: int, default is 1
The number of pods replicas. By default, the number of replicas is 1.
image: str
resources: ResourceRequirements
securityContext: corev1.SecurityContext
SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext.
When both are set, the values in SecurityContext take precedence.
podAnnotations: {str:str}
podSecurityContext: corev1.PodSecurityContext
imagePullSecrets: [corev1.LocalObjectReference]
tolerations: [corev1.Toleration]
topologySpreadConstraints: [corev1.TopologySpreadConstraint]
affinity: corev1.Affinity
service: Service
autoscaling: Autoscaling
ingress: Ingress
monitoring: Monitoring
caching: Caching
test: TestJob

Examples
--------

"""
name?: str = option("name") or "podinfo"
namespace?: str = option("namespace") or Undefined
version?: str = option("version") or "v0.1.0"
metadata: metav1.ObjectMeta = metav1.ObjectMeta {
name = name
namespace = namespace
labels: {
"app.kubernetes.io/name" = name
"app.kubernetes.io/version" = version
"app.kubernetes.io/managed-by" = "kcl"
}
}
ui: UIConfig = UIConfig {}
moduleVersion?: str = option("moduleVersion") or Undefined
kubeVersion?: str = option("kubeVersion") or Undefined
replicas: int = option("replicas") or 1
image: str = option("image") or Undefined
imagePullPolicy?: str = option("imagePullPolicy") or Undefined
resources: ResourceRequirements = ResourceRequirements {
requests: ResourceRequirement {
cpu = "100m"
memory = "320Mi"
}
}
securityContext?: corev1.SecurityContext
podAnnotations?: {str:str}
podSecurityContext?: corev1.PodSecurityContext
imagePullSecrets?: [corev1.LocalObjectReference]
tolerations?: [corev1.Toleration]
topologySpreadConstraints?: [corev1.TopologySpreadConstraint]
affinity: corev1.Affinity = corev1.Affinity {nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: [{matchExpressions: [{
key: "kubernetes.io/os"
operator: "In"
values: ["linux"]
}]}]}
service: Service = Service {}
autoscaling: Autoscaling = Autoscaling {minReplicas = replicas}
ingress: Ingress = Ingress {}
monitoring: Monitoring = Monitoring {}
caching: Caching = Caching {}
test?: TestJob

check:
replicas > 0

schema UIConfig:
color: str = "#34577c"
message?: str
backend?: str

schema ResourceRequirements:
limits?: ResourceRequirement
requests?: ResourceRequirement

check:
int(requests.cpu.split("m")[0]) <= int(limits.cpu.split("m")[0]) if limits?.cpu and requests?.cpu

schema ResourceRequirement:
cpu?: str
memory?: str

check:
regex.match(cpu, "^[1-9]\\d*m$") if cpu
regex.match(memory, "^[1-9]\\d*(Mi|Gi)$") if memory

schema Service:
port: int = 80
annotations?: {str:str} = gAnnotations
labels?: {str:str} = gLabels

check:
0 < port <= 65535

schema Autoscaling:
enabled: bool = False
cpu: int = 99
memory: str = ""
minReplicas: int
maxReplicas: int = minReplicas

check:
0 <= cpu <= 100
minReplicas <= maxReplicas if some(minReplicas) and some(maxReplicas)

schema Ingress:
enabled: bool = False
tls: bool = False
host: str = "podinfo.local"
className?: str
annotations?: {str:str} = gAnnotations
labels?: {str:str} = gLabels

schema Monitoring:
enabled: bool = False
interval: int = 15

check:
5 <= interval <= 3600

schema Caching:
enabled: bool = False
redisURL?: str

check:
regex.match(redisURL, "^tcp://.*$") if redisURL

schema TestJob:
image: str
imagePullPolicy?: str

1 change: 1 addition & 0 deletions podinfo/config/values.k
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
values: Config = option("values") or Config {image: "ghcr.io/stefanprodan/podinfo"}
7 changes: 7 additions & 0 deletions podinfo/kcl.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "podinfo"
edition = "0.0.1"
version = "0.0.1"

[dependencies]
k8s = "1.29"
9 changes: 9 additions & 0 deletions podinfo/kcl.mod.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[dependencies]
[dependencies.k8s]
name = "k8s"
full_name = "k8s_1.29"
version = "1.29"
sum = "L8gHVh822FwrQGsibx6qHRqisIekluCkMpkUO+tULXE="
reg = "ghcr.io"
repo = "kcl-lang/k8s"
oci_tag = "1.29"
12 changes: 12 additions & 0 deletions podinfo/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import templates
import manifests

manifests.yaml_stream([
templates.deployment
templates.service
templates.igress
templates.serviceAccount
templates.serviceMonitor
templates.hpa
templates.testJob
])
115 changes: 115 additions & 0 deletions podinfo/templates/deployment.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import k8s.api.apps.v1 as appsv1
import k8s.api.core.v1 as corev1
import config

values = config.values
deployment = appsv1.Deployment {
metadata: values.metadata
spec: appsv1.DeploymentSpec {
replicas = values.replicas if values.autoscaling.enabled else Undefined
strategy = {
type = "RollingUpdate"
rollingUpdate.maxUnavailable = "50%"
}
selector.matchLabels: values.metadata.labels
template.metadata: {
labels = values.metadata.labels
if values.podAnnotations:
annotations: values.podAnnotations

if values.monitoring.enabled:
annotations: {
"prometheus.io/scrape": "true"
"prometheus.io/port": "9797"
}

}
template.spec: corev1.PodSpec {
serviceAccountName = metadata.name
containers = [corev1.Container {
name: values.metadata.name
image: values.image
imagePullPolicy: values.imagePullPolicy
ports: [
{
name: "http"
containerPort: 9898
protocol: "TCP"
}
{
name: "http-metrics"
containerPort: 9797
protocol: "TCP"
}
]
livenessProbe: {
httpGet: {
path: "/healthz"
port: "http"
}
}
readinessProbe: {
httpGet: {
path: "/readyz"
port: "http"
}
}
if values.resources:
resources = corev1.ResourceRequirements {
if values.resources.limits:
limits.cpu = values.resources.limits?.cpu
limits.memory = values.resources.limits?.memory

requests.cpu = values.resources.requests?.cpu
requests.memory = values.resources.requests?.memory
}

if values.securityContext:
securityContext = values.securityContext

env: [
corev1.EnvVar {name = "PODINFO_UI_COLOR", value = values.ui.color}
if values.ui?.message:
corev1.EnvVar {name = "PODINFO_UI_MESSAGE", value = values.ui.message}

if values.ui?.backend:
corev1.EnvVar {name = "PODINFO_BACKEND_URL", value = values.ui.backend}

]
command: [
"./podinfo"
"--level=info"
"--port=9898"
"--port-metrics=9797"
if values.caching.enabled:
"--cache-server=${values.caching.redisURL}"

]
volumeMounts: [{
name: "data"
mountPath: "/data"
}]
}]
if values.podSecurityContext:
securityContext: values.podSecurityContext

if values.topologySpreadConstraints:
topologySpreadConstraints: values.topologySpreadConstraints

if values.affinity:
affinity: values.affinity

if values.tolerations:
tolerations: values.tolerations

if values.imagePullSecrets:
imagePullSecrets: values.imagePullSecrets

volumes: [{
name: "data"
emptyDir: {}
}]
}
}
}

43 changes: 43 additions & 0 deletions podinfo/templates/hpa.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import k8s.api.autoscaling.v2 as autoscaling

hpa = autoscaling.HorizontalPodAutoscaler {
apiVersion: "autoscaling/v2"
kind: "HorizontalPodAutoscaler"
metadata: values.metadata
spec: {
scaleTargetRef: {
apiVersion: "apps/v1"
kind: "Deployment"
name: values.metadata.name
}
minReplicas: values.autoscaling.minReplicas
maxReplicas: values.autoscaling.maxReplicas
metrics: [
if values.autoscaling.cpu > 0:
autoscaling.MetricSpec {
type: "Resource"
resource: {
name: "cpu"
target: {
type: "Utilization"
averageUtilization: values.autoscaling.cpu
}
}
}

if values.autoscaling.memory:
autoscaling.MetricSpec {
type: "Resource"
resource: {
name: "memory"
target: {
type: "AverageValue"
averageValue: values.autoscaling.memory
}
}
}

]
}
}

36 changes: 36 additions & 0 deletions podinfo/templates/ingress.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import k8s.api.networking.v1 as netv1

igress = netv1.Ingress {
metadata: values.metadata
metadata: {
if values.ingress.labels:
labels: values.ingress.labels

if values.ingress.annotations:
annotations: values.ingress.annotations

}
spec: netv1.IngressSpec {
rules: [{
host: values.ingress.host
http: {paths: [{
pathType: "Prefix"
path: "/"
backend.service: {
name: values.metadata.name
port.name: "http"
}
}]}
}]
if values.ingress.tls:
tls: [{
hosts: [values.ingress.host]
secretName: "${values.metadata.name}-cert"
}]

if values.ingress.className:
ingressClassName: values.ingress.className

}
}

Loading