{"id":17136627,"url":"https://github.com/joan38/kubernetes-client","last_synced_at":"2025-04-13T00:46:05.039Z","repository":{"id":25473449,"uuid":"134833115","full_name":"joan38/kubernetes-client","owner":"joan38","description":"A Kubernetes client for Scala","archived":false,"fork":false,"pushed_at":"2025-03-11T23:56:15.000Z","size":1099,"stargazers_count":126,"open_issues_count":17,"forks_count":41,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-13T00:45:53.788Z","etag":null,"topics":["client","kubernetes","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joan38.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-05-25T09:13:49.000Z","updated_at":"2025-04-09T11:23:58.000Z","dependencies_parsed_at":"2023-02-15T11:01:26.874Z","dependency_job_id":"d911d42f-db02-4e77-b3bb-6583961f67c2","html_url":"https://github.com/joan38/kubernetes-client","commit_stats":{"total_commits":276,"total_committers":20,"mean_commits":13.8,"dds":0.5579710144927537,"last_synced_commit":"fd0e441cc4684d2f4ab2a5a91296288ab71c9e35"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joan38%2Fkubernetes-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joan38%2Fkubernetes-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joan38%2Fkubernetes-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joan38%2Fkubernetes-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joan38","download_url":"https://codeload.github.com/joan38/kubernetes-client/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248650420,"owners_count":21139672,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["client","kubernetes","scala"],"created_at":"2024-10-14T20:04:53.723Z","updated_at":"2025-04-13T00:46:04.864Z","avatar_url":"https://github.com/joan38.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Kubernetes Client for Scala\n\n[![kubernetes-client Scala version support](https://index.scala-lang.org/joan38/kubernetes-client/kubernetes-client/latest-by-scala-version.svg)](https://index.scala-lang.org/joan38/kubernetes-client/kubernetes-client)\n\nA pure functional client for Kubernetes.\n\n## Installation\n[Mill](https://www.lihaoyi.com/mill):\n```scala\nivy\"com.goyeau::kubernetes-client:\u003clatest version\u003e\"\n```\nor\n\n[SBT](https://www.scala-sbt.org):\n```scala\n\"com.goyeau\" %% \"kubernetes-client\" % \"\u003clatest version\u003e\"\n```\n\n## Usage\n\n### Client configuration example\n\n#### Standard configuration \"chain\"\n\n```scala\nimport cats.effect.IO\nimport com.goyeau.kubernetes.client.*\nimport org.typelevel.log4cats.Logger\nimport org.typelevel.log4cats.slf4j.Slf4jLogger\n\nimplicit val logger: Logger[IO] = Slf4jLogger.getLogger[IO]\n\nval kubernetesClient =\n  KubernetesClient[IO](\n    KubeConfig.standard[IO]\n  )\n```\n\nThe `standard` configuration mimics the way `ClientBuilder.standard` from the official Java k8s client works:\n\n* if KUBECONFIG env variable is set, and the file exists - it will be used; the 'current-context' specified in the file\n  will be used\n* otherwise, if ~/.kube/config file exists - it will be used; the 'current-context' specified in the file will be used\n* otherwise, if cluster configuration is found - use it\n\nCluster configuration is defined by:\n\n- `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt` certificate file\n- `/var/run/secrets/kubernetes.io/serviceaccount/token` token file\n- `KUBERNETES_SERVICE_HOST` env variable (https protocol is assumed)\n- `KUBERNETES_SERVICE_PORT` env variable\n\n#### Manually providing the configuration\n\n```scala\nimport cats.effect.IO\nimport com.goyeau.kubernetes.client.*\nimport org.typelevel.log4cats.Logger\nimport org.typelevel.log4cats.slf4j.Slf4jLogger\nimport java.io.File\nimport org.http4s.AuthScheme\nimport org.http4s.Credentials.Token\nimport org.http4s.headers.Authorization\nimport org.http4s.implicits.*\nimport scala.concurrent.ExecutionContext\nimport scala.io.Source\n\nimplicit val logger: Logger[IO] = Slf4jLogger.getLogger[IO]\n\nval kubernetesClient =\n  KubernetesClient[IO](\n    KubeConfig.of[IO](\n      server = uri\"https://k8s.goyeau.com\",\n      authorization = Option(IO.pure(Authorization(Token(AuthScheme.Bearer, Source.fromFile(\"/var/run/secrets/kubernetes.io/serviceaccount/token\").mkString)))),\n      caCertFile = Option(new File(\"/var/run/secrets/kubernetes.io/serviceaccount/ca.crt\"))\n    )\n  )\n```\n\n```scala\nimport cats.effect.IO\nimport com.goyeau.kubernetes.client.*\nimport org.typelevel.log4cats.Logger\nimport org.typelevel.log4cats.slf4j.Slf4jLogger\nimport java.io.File\nimport scala.concurrent.ExecutionContext\n\nimplicit val logger: Logger[IO] = Slf4jLogger.getLogger[IO]\n\nval kubernetesClient =\n  KubernetesClient[IO](KubeConfig.fromFile[IO](new File(s\"${System.getProperty(\"user.home\")}/.kube/config\")))\n```\n\n#### Authorization caching\n\nIt is possible (and recommended) to configure the kubernetes client to cache the authorization (and renew it, when/if it\nexpires).\n\n```scala\nimport cats.effect.IO\nimport com.goyeau.kubernetes.client.*\nimport org.typelevel.log4cats.Logger\nimport org.typelevel.log4cats.slf4j.Slf4jLogger\nimport scala.concurrent.duration._\n\nimplicit val logger: Logger[IO] = Slf4jLogger.getLogger[IO]\n\nval kubernetesClient =\n  KubernetesClient[IO](\n    KubeConfig.standard[IO].map(_.withDefaultAuthorizationCache(5.minutes))\n  )\n```\n\nWhen authorization cache is configured, the client attempts to derive the expiration time of the token:\n\n* if it's a raw authorization header (provided directly, or from the token file inside the cluster), we attempt to\n  decode it as a JWT and take the `exp` field from it;\n* if authorization is provided by the auth plugin in the kube config file – the auth plugin provides the expiration\n  alongside the token.\n\nThe cache works this way:\n\nThe first time the token is \"requested\" by the client, it will unconditionally delegate to the underlying\nF[Authorization], and will cache the token.\n\n* if the underlying F[Authorization] \"throws\", the cache throws as well.\n\nWhen the token is requested subsequently:\n\n* if the expiration time is not present (the token was not a JWT, the auth plugin did not specify expiration, etc) the\n  cached authorization will be re-used forever\n* if the expiration time is present, but it's far enough into the future (later than now +\n  refreshTokenBeforeExpiration), the cached authorization will be re-used\n* if the expiration time is present, and it's soon enough (sooner than now + refreshTokenBeforeExpiration), the\n  underlying F[Authorization] will be evaluated\n  * if it's successful, the new authorization is cached\n  * if not, but the cached token is still valid, the cached token is re-used otherwise, it will raise an error.\n\nIf the cache is not configured (which is by default), the authorization will never be updated and might expire\neventually.\n\n### Requests\n\n```scala\nimport cats.effect.IO\nimport com.goyeau.kubernetes.client.*\nimport org.typelevel.log4cats.Logger\nimport org.typelevel.log4cats.slf4j.Slf4jLogger\nimport io.k8s.api.apps.v1.*\nimport io.k8s.api.core.v1.*\nimport io.k8s.apimachinery.pkg.api.resource.Quantity\nimport io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta\nimport java.io.File\nimport scala.concurrent.ExecutionContext\n\nimplicit val logger: Logger[IO] = Slf4jLogger.getLogger[IO]\n\nval kubernetesClient =\n  KubernetesClient(KubeConfig.fromFile[IO](new File(s\"${System.getProperty(\"user.home\")}/.kube/config\")))\n\nval deployment = Deployment(\n  metadata = Option(ObjectMeta(name = Option(\"web-backend\"), namespace = Option(\"my-namespace\"))),\n  spec = Option(\n    DeploymentSpec(\n      selector = null,\n      strategy = Option(\n        DeploymentStrategy(\n          `type` = Option(\"RollingUpdate\"),\n          rollingUpdate = Option(RollingUpdateDeployment(Option(StringValue(\"10%\")), Option(StringValue(\"50%\"))))\n        )\n      ),\n      template = PodTemplateSpec(\n        metadata = Option(\n          ObjectMeta(\n            labels = Option(Map(\"app\" -\u003e \"web\", \"tier\" -\u003e \"frontend\", \"environment\" -\u003e \"myenv\"))\n          )\n        ),\n        spec = Option(\n          PodSpec(\n            containers = Seq(\n              Container(\n                name = \"nginx\",\n                image = Option(\"nginx\"),\n                resources = Option(\n                  ResourceRequirements(\n                    Option(Map(\"cpu\" -\u003e Quantity(\"100m\"), \"memory\" -\u003e Quantity(\"128Mi\"))),\n                    Option(Map(\"cpu\" -\u003e Quantity(\"80m\"), \"memory\" -\u003e Quantity(\"64Mi\")))\n                  )\n                ),\n                volumeMounts = Option(Seq(VolumeMount(name = \"nginx-config\", mountPath = \"/etc/nginx/conf.d\"))),\n                ports = Option(Seq(ContainerPort(name = Option(\"http\"), containerPort = 8080)))\n              )\n            ),\n            volumes = Option(\n              Seq(\n                Volume(\n                  name = \"nginx-config\",\n                  configMap = Option(ConfigMapVolumeSource(name = Option(\"nginx-config\")))\n                )\n              )\n            )\n          )\n        )\n      )\n    )\n  )\n)\n\nkubernetesClient.use { client =\u003e\n  client.deployments.namespace(\"my-namespace\").create(deployment)\n}\n```\n\n### Raw requests\n\nIn case a particular K8S API endpoint is not explicitly supported by this library, there is an escape hatch \nthat you can use in order to run a raw request or open a raw WS connection.\n\nHere's an example of how you can get a list of nodes using a raw request: \n\n```scala\nimport cats.effect.*\nimport org.http4s.implicits.*\nimport com.goyeau.kubernetes.client.*\nimport org.http4s.*\n\nval kubernetesClient: KubernetesClient[IO] = ???\n\nval response: IO[(Status, String)] =\n  kubernetesClient\n          .raw.runRequest(\n            Request[IO](\n              uri = uri\"/api\" / \"v1\" / \"nodes\"\n            )\n          )\n          .use { response =\u003e\n            response.bodyText.foldMonoid.compile.lastOrError.map { body =\u003e\n              (response.status, body)\n            }\n          }\n```\n\nSimilarly, you can open a WS connection (`org.http4s.jdkhttpclient.WSConnectionHighLevel`):\n\n```scala\nimport cats.effect.*\nimport org.http4s.implicits.*\nimport com.goyeau.kubernetes.client.*\nimport org.http4s.*\nimport org.http4s.jdkhttpclient.*\n\nval connection: Resource[IO, WSConnectionHighLevel[IO]] =\n  kubernetesClient.raw.connectWS(\n    WSRequest(\n      uri = (uri\"/api\" / \"v1\" / \"my-custom-thing\") +? (\"watch\" -\u003e \"true\")\n    )\n  )\n```\n\n## Development\n\n### Pre-requisites\n\n - Java 11 or higher\n - Docker\n\n### IntelliJ\n\nGenerate a BSP configuration:\n\n```shell\n./mill mill.bsp.BSP/install\n```\n\n### Compiling\n\n```shell\n./mill kubernetes-client[2.13.15].compile\n```\n\n### Running the tests\n\nAll tests:\n\n```shell\n./mill kubernetes-client[2.13.15].test\n```\n\nA specific test:\n\n```shell\n./mill kubernetes-client[2.13.15].test.testOnly 'com.goyeau.kubernetes.client.api.PodsApiTest'\n```\n\n[minikube](https://minikube.sigs.k8s.io/docs/) has to be installed and running.\n\n### Before opening a PR:\n\nCheck and fix formatting:\n\n```shell\n./mill __.style\n```\n\n\n\n\n## Related projects\n\n* [Skuber](https://github.com/doriordan/skuber)\n* [Kubernetes Client for Java](https://github.com/kubernetes-client/java)\n\n\n## Why Kubernetes Client for Scala?\n\nYou might wonder why using this library instead of Skuber for example? Kubernetes Client is a pure functional based on\nCats and Http4s.  \nAnother benefit of Kubernetes Client is that (like the [Kubernetes Client for Java](https://github.com/kubernetes-client/java/#update-the-generated-code))\nit is generating all the payload case classes by just ingesting the swagger api provided by Kubernetes' main repo. That\nmeans this project will always remain up to date with the latest Kubernetes API.\n\n## Adopters/Projects\n\n* [Kerberos-Operator2](https://github.com/novakov-alexey/krb-operator2)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoan38%2Fkubernetes-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoan38%2Fkubernetes-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoan38%2Fkubernetes-client/lists"}