local baseKube = import "k8s/configs/k.libsonnet"; local tanka = import "tanka-util/main.libsonnet"; local helm = tanka.helm.new(std.thisFile); local baseKubeCompat = { _Object(apiVersion, kind, name):: { local this = self, apiVersion: apiVersion, kind: kind, metadata: { name: name, labels: { name: std.join("-", std.split(this.metadata.name, ":")) }, annotations: {}, }, }, }; local splitThisFilePath = std.split(std.thisFile, "/"); local workspaceRoot = std.join('/', splitThisFilePath[:std.length(splitThisFilePath)-3]); local workspaceRootLength = std.length(workspaceRoot); { helm: helm, // Returns array of values from given object. Does not include hidden fields. objectValues(o):: [o[field] for field in std.objectFields(o)], asWorkspacePath(fullPath): local isPrefixed = std.startsWith(fullPath,workspaceRoot); if !isPrefixed then error fullPath + "is not prefixed by workspace root \"" + workspaceRoot + "\"" else fullPath[workspaceRootLength+1 :], errNeedField(name): error name + " must be defined", SimpleFieldStruct(requiredFields): { [requiredField]: $.errNeedField(requiredField) for requiredField in requiredFields }, simpleFieldStruct: $.SimpleFieldStruct, Context: $.SimpleFieldStruct([ "helm" ]), NewContext(helm): $.Context { helm: helm }, List(): { apiVersion: "v1", kind: "List", items_:: {}, items: $.objectValues(self.items_), }, ################# # Support utils # ################# # For use in SERVICE.metadata.annotations position # TODO(acmcarther): move this into PrometheusScrapeable(port): { "prometheus.io/scrape": "true", "prometheus.io/port": std.toString(port), }, NameVal(name, value): { name: name, value: value, }, SvcUtil: { # For use in SERVICE.spec.ports position TCPServicePort(name, port): { name: name, port: port, protocol: "TCP", }, # For use in SERVICE.spec.ports position UDPServicePort(name, port): { name: name, port: port, protocol: "UDP", }, # For use in SERVICE.spec BasicHttpClusterIpSpec(internalPort): { type: "ClusterIP", ports: [ $.SvcUtil.TCPServicePort("http", 80) { targetPort: internalPort }, ], }, # For use in SERVICE.spec BasicNodePortSpec(internalPort, nodePort): { type: "NodePort", ports: [ $.SvcUtil.TCPServicePort("tcp", internalPort) { targetPort: internalPort, nodePort: nodePort, }, $.SvcUtil.UDPServicePort("udp", internalPort) { targetPort: internalPort, nodePort: nodePort, }, ], } }, DeployUtil: { # For use in DEPLOYMENT.spec.template.spec.containers VolumeMount(name, mountPath): { name: name, mountPath: mountPath, }, # For use in DEPLOYMENT.spec.template.spec.volumes VolumeClaimRef(name, claimName): { name: name, persistentVolumeClaim: { claimName: claimName, }, }, # For use in DEPLOYMENT.spec.template.spec.containers.ports ContainerPort(name, port): { name: name, containerPort: port, }, ContainerTCPPort(name, port): { name: name, containerPort: port, protocol: "TCP", }, # For use in DEPLOYMENT.spec.strategy SimpleRollingUpdate(): { type: "RollingUpdate", rollingUpdate: { maxUnavailable: 1, }, }, # For use in DEPLOYMENT.spec.template.spec.*Probe SimpleProbe(webPort, delaySeconds): { httpGet: { path: "/", port: webPort, }, initialDelaySeconds: delaySeconds, }, }, ########################## # Kube object definition # ########################## Namespace(name): baseKube.Namespace(name), StorageClass(name): baseKubeCompat._Object("storage.k8s.io/v1", "StorageClass", name) { }, RecoverableSimplePvc(namespace, name, storageClass, quantity, recoverySpec): { pv: if recoverySpec == null then {} else $.SimplePv(namespace, storageClass, quantity, recoverySpec.volumeName) { spec+: { nfs: { path: recoverySpec.nfsPath, server: recoverySpec.nfsServer, } } }, pvc: $.SimplePvc(namespace, name, storageClass, quantity) { spec+: { volumeName: if recoverySpec == null then null else recoverySpec.volumeName, } } }, RecoverableSimpleManyPvc(namespace, name, storageClass, quantity, recoverySpec): { pv: if recoverySpec == null then {} else $.SimpleManyPv(namespace, storageClass, quantity, recoverySpec.volumeName) { spec+: { nfs: { path: recoverySpec.nfsPath, server: recoverySpec.nfsServer, } } }, pvc: $.SimpleManyPvc(namespace, name, storageClass, quantity) { spec+: { volumeName: if recoverySpec == null then null else recoverySpec.volumeName, } } }, # N.B. Actual storage must still be defined. SimplePv(namespace, storageClass, quantity, name): baseKubeCompat._Object("v1", "PersistentVolume", name) { metadata+: { namespace: namespace, }, spec+: { storageClassName: storageClass, volumeMode: "Filesystem", persistentVolumeReclaimPolicy: "Delete", capacity: { storage: quantity, }, accessModes: ["ReadWriteOnce"], }, }, SimpleManyPv(namespace, storageClass, quantity, name): $.SimplePv(namespace, storageClass, quantity, name) { spec+: { accessModes: ["ReadWriteMany"], }, }, SimplePvc(namespace, name, storageClass, quantity): baseKubeCompat._Object("v1", "PersistentVolumeClaim", name) { metadata+: { namespace: namespace, }, spec: { storageClassName: storageClass, resources: { requests: { storage: quantity, }, }, accessModes: ["ReadWriteOnce"], }, }, SimpleManyPvc(namespace, name, storageClass, quantity): $.SimplePvc(namespace, name, storageClass, quantity) { spec+: { accessModes: ["ReadWriteMany"], }, }, Role(namespace, name): baseKubeCompat._Object("rbac.authorization.k8s.io/v1", "Role", name) { metadata+: { namespace: namespace, }, }, RoleBinding(namespace, name): baseKubeCompat._Object("rbac.authorization.k8s.io/v1", "RoleBinding", name) { metadata+: { namespace: namespace, }, }, ConfigMap(namespace, name): baseKubeCompat._Object("v1", "ConfigMap", name) { metadata+: { namespace: namespace, }, }, ClusterRole(name): baseKubeCompat._Object("rbac.authorization.k8s.io/v1", "ClusterRole", name) { }, ClusterRoleBinding(name): baseKubeCompat._Object("rbac.authorization.k8s.io/v1", "ClusterRoleBinding", name) { }, DaemonSet(namespace, name): baseKubeCompat._Object("apps/v1", "DaemonSet", name) { metadata+: { namespace: namespace, }, }, ServiceAccount(namespace, name): baseKubeCompat._Object("v1", "ServiceAccount", name) { metadata+: { namespace: namespace, }, }, Service(namespace, name): baseKubeCompat._Object("v1", "Service", name) { metadata+: { namespace: namespace, }, # TODO(acmcarther): Figure out how to make this more useful }, Deployment(namespace, name): baseKubeCompat._Object("apps/v1", "Deployment", name) { metadata+: { namespace: namespace, }, }, StatefulSet(namespace, name): baseKubeCompat._Object("apps/v1", "StatefulSet", name) { metadata+: { namespace: namespace, }, }, Ingress(namespace, name): baseKubeCompat._Object("networking.k8s.io/v1", "Ingress", name) { metadata+: { namespace: namespace, }, }, Secret(namespace, name): baseKubeCompat._Object("v1", "Secret", name) { metadata+: { namespace: namespace, }, }, CronJob(namespace, name): baseKubeCompat._Object("batch/v1", "CronJob", name) { metadata+: { namespace: namespace, }, }, Endpoints(namespace, name): baseKubeCompat._Object("v1", "Endpoints", name) { metadata+: { namespace: namespace, }, }, EndpointSlice(namespace, name): baseKubeCompat._Object("discovery.k8s.io/v1", "EndpointSlice", name) { metadata+: { namespace: namespace, }, }, ApiService(name): baseKubeCompat._Object("apiregistration.k8s.io/v1", "APIService", name) {}, IngressClass(name): baseKubeCompat._Object("networking.k8s.io/v1", "IngressClass", name) {}, ClusterIssuer(namespace, name): baseKubeCompat._Object("cert-manager.io/v1", "ClusterIssuer", name) { metadata+: { namespace: namespace, }, }, # Provides "curried" functions with namespace pre-bound UsingNamespace(namespace): { Endpoints(name): $.Endpoints(namespace, name), EndpointSlice(name): $.EndpointSlice(namespace, name), SimplePvc(name, storageClass, quantity): $.SimplePvc(namespace, name, storageClass, quantity), SimpleManyPvc(name, storageClass, quantity): $.SimpleManyPvc(namespace, name, storageClass, quantity), ServiceAccount(name): $.ServiceAccount(namespace, name), Service(name): $.Service(namespace, name), Deployment(name): $.Deployment(namespace, name), CronJob(name): $.CronJob(namespace, name), StatefulSet(name): $.StatefulSet(namespace, name), Ingress(name): $.Ingress(namespace, name), ConfigMap(name): $.ConfigMap(namespace, name), }, }