Files
weave-scope/probe/kubernetes/client.go
2016-05-11 11:19:15 +01:00

269 lines
7.6 KiB
Go

package kubernetes
import (
"io"
"strconv"
"sync"
"time"
log "github.com/Sirupsen/logrus"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/cache"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/util/wait"
)
// Client keeps track of running kubernetes pods and services
type Client interface {
Stop()
WalkPods(f func(Pod) error) error
WalkServices(f func(Service) error) error
WalkDeployments(f func(Deployment) error) error
WalkReplicaSets(f func(ReplicaSet) error) error
WalkReplicationControllers(f func(ReplicationController) error) error
WalkNodes(f func(*api.Node) error) error
WatchPods(f func(Event, Pod))
GetLogs(namespaceID, podID string) (io.ReadCloser, error)
DeletePod(namespaceID, podID string) error
ScaleUp(resource, namespaceID, id string) error
ScaleDown(resource, namespaceID, id string) error
}
type client struct {
quit chan struct{}
resyncPeriod time.Duration
client *unversioned.Client
extensionsClient *unversioned.ExtensionsClient
podStore *cache.StoreToPodLister
serviceStore *cache.StoreToServiceLister
deploymentStore *cache.StoreToDeploymentLister
replicaSetStore *cache.StoreToReplicaSetLister
replicationControllerStore *cache.StoreToReplicationControllerLister
nodeStore *cache.StoreToNodeLister
podWatchesMutex sync.Mutex
podWatches []func(Event, Pod)
}
// runReflectorUntil is equivalent to cache.Reflector.RunUntil, but it also logs
// errors, which cache.Reflector.RunUntil simply ignores
func runReflectorUntil(r *cache.Reflector, resyncPeriod time.Duration, stopCh <-chan struct{}) {
loggingListAndWatch := func() {
if err := r.ListAndWatch(stopCh); err != nil {
log.Errorf("Kubernetes reflector: %v", err)
}
}
go wait.Until(loggingListAndWatch, resyncPeriod, stopCh)
}
// NewClient returns a usable Client. Don't forget to Stop it.
func NewClient(addr string, resyncPeriod time.Duration) (Client, error) {
var config *restclient.Config
if addr != "" {
config = &restclient.Config{Host: addr}
} else {
// If no API server address was provided, assume we are running
// inside a pod. Try to connect to the API server through its
// Service environment variables, using the default Service
// Account Token.
var err error
if config, err = restclient.InClusterConfig(); err != nil {
return nil, err
}
}
c, err := unversioned.New(config)
if err != nil {
return nil, err
}
ec, err := unversioned.NewExtensions(config)
if err != nil {
return nil, err
}
result := &client{
quit: make(chan struct{}),
resyncPeriod: resyncPeriod,
client: c,
extensionsClient: ec,
}
result.podStore = &cache.StoreToPodLister{Store: result.setupStore(c, "pods", &api.Pod{})}
result.serviceStore = &cache.StoreToServiceLister{Store: result.setupStore(c, "services", &api.Service{})}
result.replicationControllerStore = &cache.StoreToReplicationControllerLister{Store: result.setupStore(c, "replicationcontrollers", &api.ReplicationController{})}
result.nodeStore = &cache.StoreToNodeLister{Store: result.setupStore(c, "nodes", &api.Node{})}
// We list deployments here to check if this version of kubernetes is >= 1.2.
// We would use NegotiateVersion, but Kubernetes 1.1 "supports"
// extensions/v1beta1, but not deployments or replicasets.
if _, err := ec.Deployments(api.NamespaceAll).List(api.ListOptions{}); err != nil {
log.Infof("Deployments and ReplicaSets are not supported by this Kubernetes version")
} else {
result.deploymentStore = &cache.StoreToDeploymentLister{Store: result.setupStore(ec, "deployments", &extensions.Deployment{})}
result.replicaSetStore = &cache.StoreToReplicaSetLister{Store: result.setupStore(ec, "replicasets", &extensions.ReplicaSet{})}
}
return result, nil
}
func (c *client) setupStore(kclient cache.Getter, resource string, itemType interface{}) cache.Store {
lw := cache.NewListWatchFromClient(kclient, resource, api.NamespaceAll, fields.Everything())
store := cache.NewStore(cache.MetaNamespaceKeyFunc)
runReflectorUntil(cache.NewReflector(lw, itemType, store, c.resyncPeriod), c.resyncPeriod, c.quit)
return store
}
func (c *client) WatchPods(f func(Event, Pod)) {
c.podWatchesMutex.Lock()
defer c.podWatchesMutex.Unlock()
c.podWatches = append(c.podWatches, f)
}
func (c *client) triggerPodWatches(e Event, pod interface{}) {
c.podWatchesMutex.Lock()
defer c.podWatchesMutex.Unlock()
for _, watch := range c.podWatches {
watch(e, NewPod(pod.(*api.Pod)))
}
}
func (c *client) WalkPods(f func(Pod) error) error {
pods, err := c.podStore.List(labels.Everything())
if err != nil {
return err
}
for _, pod := range pods {
if err := f(NewPod(pod)); err != nil {
return err
}
}
return nil
}
func (c *client) WalkServices(f func(Service) error) error {
list, err := c.serviceStore.List()
if err != nil {
return err
}
for i := range list.Items {
if err := f(NewService(&(list.Items[i]))); err != nil {
return err
}
}
return nil
}
func (c *client) WalkDeployments(f func(Deployment) error) error {
if c.deploymentStore == nil {
return nil
}
list, err := c.deploymentStore.List()
if err != nil {
return err
}
for i := range list {
if err := f(NewDeployment(&(list[i]))); err != nil {
return err
}
}
return nil
}
// WalkReplicaSets calls f for each replica set
func (c *client) WalkReplicaSets(f func(ReplicaSet) error) error {
if c.replicaSetStore == nil {
return nil
}
list, err := c.replicaSetStore.List()
if err != nil {
return err
}
for i := range list {
if err := f(NewReplicaSet(&(list[i]))); err != nil {
return err
}
}
return nil
}
// WalkReplicationcontrollers calls f for each replication controller
func (c *client) WalkReplicationControllers(f func(ReplicationController) error) error {
list, err := c.replicationControllerStore.List()
if err != nil {
return err
}
for i := range list {
if err := f(NewReplicationController(&(list[i]))); err != nil {
return err
}
}
return nil
}
func (c *client) WalkNodes(f func(*api.Node) error) error {
list, err := c.nodeStore.List()
if err != nil {
return err
}
for i := range list.Items {
if err := f(&(list.Items[i])); err != nil {
return err
}
}
return nil
}
func (c *client) GetLogs(namespaceID, podID string) (io.ReadCloser, error) {
return c.client.RESTClient.Get().
Namespace(namespaceID).
Name(podID).
Resource("pods").
SubResource("log").
Param("follow", strconv.FormatBool(true)).
Param("previous", strconv.FormatBool(false)).
Param("timestamps", strconv.FormatBool(true)).
Stream()
}
func (c *client) DeletePod(namespaceID, podID string) error {
return c.client.RESTClient.Delete().
Namespace(namespaceID).
Name(podID).
Resource("pods").Do().Error()
}
func (c *client) ScaleUp(resource, namespaceID, id string) error {
return c.modifyScale(resource, namespaceID, id, func(scale *extensions.Scale) {
scale.Spec.Replicas++
})
}
func (c *client) ScaleDown(resource, namespaceID, id string) error {
return c.modifyScale(resource, namespaceID, id, func(scale *extensions.Scale) {
scale.Spec.Replicas--
})
}
func (c *client) modifyScale(resource, namespace, id string, f func(*extensions.Scale)) error {
scaler := c.extensionsClient.Scales(namespace)
scale, err := scaler.Get(resource, id)
if err != nil {
return err
}
f(scale)
_, err = scaler.Update(resource, scale)
return err
}
func (c *client) Stop() {
close(c.quit)
}