154 строки
4.4 KiB
Go
154 строки
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"time"
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"log"
|
|
"reflect"
|
|
|
|
"k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/fields"
|
|
"k8s.io/client-go/kubernetes"
|
|
"k8s.io/client-go/rest"
|
|
"k8s.io/client-go/tools/cache"
|
|
)
|
|
|
|
type Pod struct {
|
|
Container string
|
|
ContainerImage string
|
|
Name string
|
|
Namespace string
|
|
Status string
|
|
Action string
|
|
}
|
|
|
|
func main() {
|
|
log.Println("Starting up Captain Kube")
|
|
informerChannel := make(chan struct{})
|
|
go runinformer(informerChannel)
|
|
|
|
runhealthz()
|
|
<-informerChannel
|
|
log.Println("Captain Kube shutting down")
|
|
}
|
|
|
|
func runhealthz() {
|
|
// Start listening for health checks
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/healthz", func(w http.ResponseWriter, req *http.Request) {
|
|
checkReq, err := http.NewRequest(http.MethodGet, "http://parrot-parrot/", bytes.NewBuffer([]byte(``)))
|
|
httpclient := &http.Client{}
|
|
_, err = httpclient.Do(checkReq)
|
|
if err != nil {
|
|
log.Println("Parrot is unreachable")
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
} else {
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
})
|
|
|
|
log.Println("Listening for health checks...")
|
|
http.ListenAndServe(":3000", mux)
|
|
}
|
|
|
|
func runinformer(done chan struct{}) {
|
|
// creates the in-cluster config
|
|
config, err := rest.InClusterConfig()
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
|
|
// creates the client
|
|
client, err := kubernetes.NewForConfig(config)
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
|
|
// Clear the cluster status, start with a blank slate
|
|
req, err := http.NewRequest(http.MethodDelete, "http://parrot-parrot/api/ClusterStatus", bytes.NewBuffer([]byte(``)))
|
|
httpclient := &http.Client{}
|
|
_, err = httpclient.Do(req)
|
|
if err != nil {
|
|
log.Printf("The HTTP request failed with error %s", err)
|
|
} else {
|
|
log.Printf("\n\n**** Cleared parrot****\n\n")
|
|
}
|
|
|
|
watchList := cache.NewListWatchFromClient(client.Core().RESTClient(), "pods", v1.NamespaceAll, fields.Everything())
|
|
|
|
// Setup the informer that will start watching for pod triggers
|
|
informer := cache.NewSharedIndexInformer(
|
|
watchList,
|
|
&v1.Pod{},
|
|
10*time.Second,
|
|
cache.Indexers{},
|
|
) // We only want `Pod`, force resync every 10 seconds
|
|
|
|
// Setup the trigger handlers that will receive triggers
|
|
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
|
// This method is executed when a new pod is created
|
|
AddFunc: func(obj interface{}) {
|
|
pod, ok := obj.(*v1.Pod) // cast the object as a pod
|
|
if !ok {
|
|
//log.Printf("Couldn't cast object as pod: %s", obj)
|
|
return
|
|
}
|
|
pingparrot(pod,"Added") // Ping the parrot
|
|
},
|
|
// This method is executed when an existing pod is updated
|
|
UpdateFunc: func(oldObj, newObj interface{}) {
|
|
newPod, ok := newObj.(*v1.Pod) // cast the object as a pod
|
|
if !ok {
|
|
//log.Printf("Couldn't cast object as pod: %s", newObj)
|
|
return
|
|
}
|
|
// Deep compare objects and only notify if they are truly different
|
|
if !reflect.DeepEqual(oldObj, newObj) {
|
|
pingparrot(newPod,"Updated") // Ping the parrot
|
|
}
|
|
},
|
|
// This method is executed when an existing pod is deleted
|
|
DeleteFunc: func(obj interface{}) {
|
|
pod, ok := obj.(*v1.Pod) // cast the object as a pod
|
|
if !ok {
|
|
//log.Printf("Couldn't cast object as pod: %s", obj)
|
|
return
|
|
}
|
|
pingparrot(pod,"Deleted") // Ping the parrot
|
|
},
|
|
})
|
|
|
|
// Start the informer, until `done` is closed
|
|
informer.Run(done)
|
|
}
|
|
|
|
func pingparrot(pod *v1.Pod, state string) {
|
|
if pod.ObjectMeta.Namespace != "kube-system" {
|
|
log.Printf("Pod %s: %s", state, pod.ObjectMeta.Name)
|
|
log.Printf("namespace: %s", pod.ObjectMeta.Namespace)
|
|
log.Printf("status: %s", pod.Status.Phase)
|
|
log.Printf("startTime: %s", pod.Status.StartTime)
|
|
log.Printf("conditions:")
|
|
|
|
for _, condition := range pod.Status.Conditions {
|
|
log.Printf("\ttype: %s", condition.Type)
|
|
log.Printf("\tlastTransitionTime: %s", condition.LastTransitionTime)
|
|
}
|
|
|
|
// shrink the object we send over
|
|
p := Pod{Action: state, Container: pod.Spec.Containers[0].Name, ContainerImage: pod.Spec.Containers[0].Image, Name: pod.ObjectMeta.Name, Namespace: pod.ObjectMeta.Namespace, Status: string(pod.Status.Phase)}
|
|
|
|
jsonValue, _ := json.Marshal(p)
|
|
//log.Printf("\n%s\n",jsonValue)
|
|
|
|
_, err := http.Post("http://parrot-parrot/api/ClusterStatus", "application/json", bytes.NewBuffer(jsonValue))
|
|
if err != nil {
|
|
log.Printf("The HTTP request failed with error %s", err)
|
|
} else {
|
|
log.Printf("Notified parrot: %s", state)
|
|
}
|
|
log.Printf("\n\n")
|
|
}
|
|
} |