[
  {
    "path": "README.md",
    "content": "![](img/banner.png)\n\nKubernetes bash scheduler\n============\n\n### This is a really bad idea\n\nI made it because it was helpful for me to learn :bowtie:\nI hope you learn something too.\n\n## How to run it \n\nPlease don't ever use this on a production kubernetes cluster!!!\n\nRun it against a local [kind](https://kind.sigs.k8s.io/) cluster.\n\n```\nkind create cluster\n```\n\nCreate pods that use the custom scheduler.\n\n```\nkubectl apply -f https://raw.githubusercontent.com/rothgar/bashScheduler/main/nginx.deploy.yaml\n```\n\nYou should see nginx pods with Pending status\n\n```\nNAME                     READY   STATUS    RESTARTS   AGE\nnginx-56dcc974bc-8ss4m   0/1     Pending   0          49m                                              \nnginx-56dcc974bc-94ltw   0/1     Pending   0          49m                                              \nnginx-56dcc974bc-tnz6s   0/1     Pending   0          49m \n```\n\nThen proxy your localhost to the kubernetes api server\n\n```\nkubectl proxy\nStarting to serve on 127.0.0.1:8001\n```\n\nNow in a new terminal run\n\n```\ncurl -sL https://raw.githubusercontent.com/rothgar/bashScheduler/main/scheduler.sh | bash\n```\n\nYou should see similar output to\n```\nAssigned nginx-56dcc974bc-8ss4m to kind-control-plane\nAssigned nginx-56dcc974bc-94ltw to kind-control-plane\nAssigned nginx-56dcc974bc-tnz6s to kind-control-plane\n```\n\nLook at the code and see what it's doing.\nUncomment `set -x` to see all the commands run.\n\nHave fun :shipit:\n"
  },
  {
    "path": "nginx.deploy.yaml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: nginx\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: nginx\n  template:\n    metadata:\n      labels:\n        app: nginx\n    spec:\n      schedulerName: bashScheduler\n      containers:\n      - name: nginx\n        image: nginx\n        resources:\n          limits:\n            memory: \"128Mi\"\n            cpu: \"500m\"\n        ports:\n        - containerPort: 80"
  },
  {
    "path": "scheduler.sh",
    "content": "#!/usr/bin/env bash\n#\n# A kubernetes scheduler written in bash\n# dependencies: kubectl, curl\n# Runs against localhost:8001 by default\n# Use `kubectl proxy` to proxy to master without https\n#\n# Author: Justin Garrison\n# justingarrison.com\n# @rothgar\n\nset -eo pipefail\n# uncomment to see all commands in stdout\n# set -x\n\nSERVER=\"${SERVER:-localhost:8001}\"\nSCHEDULER=\"${SCHEDULER:-bashScheduler}\"\n\nwhile true; do\n  # Get a list of all our pods in pending state\n  for POD in $(kubectl --server ${SERVER} get pods \\\n              --output jsonpath='{.items..metadata.name}' \\\n              --all-namespaces \\\n              --field-selector=status.phase==Pending); do\n\n    SCHEDULER_NAME=$(kubectl get pod ${POD} \\\n                    --output jsonpath='{.spec.schedulerName}')\n\n    if [ \"${SCHEDULER_NAME}\" == \"${SCHEDULER}\" ]; then\n      # Get the pod namespace\n      NAMESPACE=$(kubectl get pod ${POD} \\\n                  --output jsonpath='{.metadata.namespace}')\n\n      # Get an array for all of the nodes\n      # We could optionally check if the nodes are ready\n      NODES=($(kubectl --server ${SERVER} get nodes \\\n              --output jsonpath='{.items..metadata.name}'))\n\n      # Store a number for the length of our NODES array\n      NODES_LENGTH=${#NODES[@]}\n\n      # Randomly select a node from the array\n      # $RANDOM % $NODES_LENGTH will be the remainder\n      # of a random number divided by the length of our nodes\n      # In the case of 1 node this is always ${NODES[0]}\n      NODE=${NODES[$[$RANDOM % $NODES_LENGTH]]}\n\n      # Bind the current pod to the node selected\n      curl --silent --fail \\\n        --header \"Content-Type:application/json\" \\\n        --request POST \\\n        --data '{\"apiVersion\":\"v1\",\n                \"kind\": \"Binding\", \n                \"metadata\": {\n                  \"name\": \"'${POD}'\"\n                  }, \n                \"target\": {\n                  \"apiVersion\": \"v1\", \n                  \"kind\": \"Node\", \n                  \"name\": \"'${NODE}'\"\n                  }\n                }' \\\n        http://${SERVER}/api/v1/namespaces/${NAMESPACE}/pods/${POD}/binding/ >/dev/null \\\n        && echo \"Assigned ${POD} to ${NODE}\" \\\n        || echo \"Failed to assign ${POD} to ${NODE}\"\n    fi\n  done\n  echo \"Nothing to do...sleeping.\"\n  sleep 6s\ndone\n"
  }
]