Use QLKube to query the Kubernetes API
QLKube is a project that exposes the Kubernetes API as GraphQL. GraphQL is a data query and manipulation language for APIs developed initially by Facebook and released as open-source. It strives to reduce the chattiness clients can experience when querying REST APIs. It is very useful for mobile application and web development: by reducing the number of roundtrips needed to fetch the relevant data and by fetching only the needed field, the network usage is greatly reduced.
To install QLKube in OpenShift, use the NodeJS Source-to-Image builder:
oc new-project qlkube --display-name=QLKube
oc new-app nodejs~https://github.com/qlkube/qlkube.git --name=qlkube
Disable TLS certificate validation to accommodate your self-signed certificates:
oc set env dc/qlkube NODE_TLS_REJECT_UNAUTHORIZED=0
And enable the NodeJS development mode to enable the GraphQL explorer (disabled in production mode):
oc set env dc/qlkube NODE_ENV=development
Give the GLKube’s Service Account the right to query the Kubernetes API for its own namespace:
oc adm policy add-role-to-user view -z default
Once deployed, open the QLKube URL in your web browser:
open $(oc get route qlkube -o go-template --template="http://{{.spec.host}}")
You can try the following queries in the GraphQL explorer.
Get all pods in the current namespace
Unless you gave the cluster-admin
right to the QLKube Service Account, you will have to specify a target namespace in all your queries. The all
type is a meta type defined by QLKube to ease the use of common types such as services
, deployments
, pods
, daemonSets
, replicaSets
, statefulSets
, jobs
or cronJobs
.
Query:
query getAllPodsInCurrentNamespace {
all(namespace: "qlkube") {
pods {
items {
metadata {
name
creationTimestamp
}
status {
phase
}
}
}
}
}
Response:
{
"data": {
"all": {
"pods": {
"items": [
{
"metadata": {
"name": "qlkube-1-build",
"creationTimestamp": "2019-06-07T07:56:53Z"
},
"status": {
"phase": "Succeeded"
}
},
{
"metadata": {
"name": "qlkube-3-jplpc",
"creationTimestamp": "2019-06-07T14:03:48Z"
},
"status": {
"phase": "Running"
}
}
]
}
}
}
}
Get a service by name
To get an object by name, you can use the fieldSelector
parameter (in this example, we are filtering on the name
field in the metadata
section).
Query:
query getServiceByNameAndNamespace {
all(namespace: "qlkube", fieldSelector: "metadata.name=qlkube") {
services {
items{
metadata {
name
namespace
}
spec {
clusterIP
}
}
}
}
}
Response:
{
"data": {
"all": {
"services": {
"items": [
{
"metadata": {
"name": "qlkube",
"namespace": "qlkube"
},
"spec": {
"clusterIP": "172.30.213.61"
}
}
]
}
}
}
}
Type introspection
Playing with the built-in types of GLKube is nice but you might soon be limited. To discover all the available types, run this query:
{
__schema {
types {
name
}
}
}
This query returns a list of all the available types (truncated here for brevity):
{
"data": {
"__schema": {
"types": [
{
"name": "Query"
},
{
"name": "String"
},
{
"name": "Boolean"
},
{
"name": "Int"
},
{
"name": "ComGithubOpenshiftApiAppsV1DeploymentConfig"
},
{
"name": "ComGithubOpenshiftApiRouteV1Route"
},
{
"name": "ComGithubOpenshiftApiRouteV1RouteList"
}
]
}
}
}
Get a Deployment Config by name and namespace
Once the desired data type discovered, you can use it directly.
- If the data type represents an item (such as
ComGithubOpenshiftApiRouteV1Route
) you will need to specify thename
andnamespace
parameters. - If the data type is a list (such as
ComGithubOpenshiftApiRouteV1RouteList
) you will need to specify thenamespace
and optionally afieldSelector
parameters.
Query:
query getDeploymentConfigByNameAndNamespace {
comGithubOpenshiftApiAppsV1DeploymentConfig(name: "qlkube", namespace: "qlkube") {
metadata {
name
}
status {
replicas
availableReplicas
}
}
}
Reponse:
{
"data": {
"comGithubOpenshiftApiAppsV1DeploymentConfig": {
"metadata": {
"name": "qlkube"
},
"status": {
"replicas": 1,
"availableReplicas": 1
}
}
}
}
Get routes by hostname and namespace
This query use a fieldSelector
on the host
field in the spec
section and uses aliasing to rename the status
field.
Query:
query getRouteByHostnameAndNamespace {
routes: comGithubOpenshiftApiRouteV1RouteList(namespace: "qlkube" fieldSelector: "spec.host=qlkube-qlkube.app.itix.fr") {
items {
metadata {
name
}
status {
ingress {
routerName
conditions {
deployed: status
}
}
}
}
}
}
Reponse:
{
"data": {
"routes": {
"items": [
{
"metadata": {
"name": "qlkube"
},
"status": {
"ingress": [
{
"routerName": "router",
"conditions": [
{
"deployed": "True"
}
]
}
]
}
}
]
}
}
}
Sending your GraphQL request from curl
Once your GraphQL queries refined in the GraphQL Explorer, you can send them directly using curl or any HTTP client.
export GLKUBE_HOSTNAME=$(oc get route qlkube -o go-template --template="{{.spec.host}}")
cat <<EOF | curl -XPOST "http://$GLKUBE_HOSTNAME/" -H "Content-Type: application/json" -d @- -s |jq .
{
"query": "query getAllPodsInCurrentNamespace {
all(namespace: \"qlkube\") {
services {
items {
metadata {
name
namespace
creationTimestamp
labels
}
}
}
}
}"
}
EOF
Advanced use-cases
One use case in which GraphQL is very interesting is the ability to request in the same query an object and it’s linked objects. For instance, it would be nice from a hostname to query the route that matches this hostname, along with the service backing this route and the pods behind the service.
Unfortunately, this is not yet possible with GLKube. Since it auto-generates its GraphQL schema from the OpenAPI Specifications of the Kubernetes APIs and those APIs are loosely coupled, some code would be required to link the relevant object between them.
Conclusion
GLKube is a nice initiative to ease the use of the Kubernetes API. Definitely worth checking from times to times the status of this project. Some more code would be needed to enable the really interesting use cases that GraphQL can bring.