2014-10-11 00:46:38 +00:00
|
|
|
/*
|
|
|
|
Copyright 2014 Google Inc. All rights reserved.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package record
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
2014-11-21 00:01:42 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
2014-10-11 00:46:38 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
|
|
|
|
|
|
|
"github.com/golang/glog"
|
|
|
|
)
|
|
|
|
|
|
|
|
// EventRecorder knows how to store events (client.Client implements it.)
|
2014-10-30 00:27:11 +00:00
|
|
|
// EventRecorder must respect the namespace that will be embedded in 'event'.
|
2014-11-21 00:01:42 +00:00
|
|
|
// It is assumed that EventRecorder will return the same sorts of errors as
|
|
|
|
// pkg/client's REST client.
|
2014-10-11 00:46:38 +00:00
|
|
|
type EventRecorder interface {
|
2014-10-21 21:14:35 +00:00
|
|
|
Create(event *api.Event) (*api.Event, error)
|
2014-10-11 00:46:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// StartRecording starts sending events to recorder. Call once while initializing
|
|
|
|
// your binary. Subsequent calls will be ignored. The return value can be ignored
|
|
|
|
// or used to stop recording, if desired.
|
2015-01-06 21:22:58 +00:00
|
|
|
func StartRecording(recorder EventRecorder, source api.EventSource) watch.Interface {
|
2014-10-11 00:46:38 +00:00
|
|
|
return GetEvents(func(event *api.Event) {
|
|
|
|
// Make a copy before modification, because there could be multiple listeners.
|
|
|
|
// Events are safe to copy like this.
|
|
|
|
eventCopy := *event
|
|
|
|
event = &eventCopy
|
2015-01-06 21:22:58 +00:00
|
|
|
event.Source = source
|
2014-11-21 00:01:42 +00:00
|
|
|
try := 0
|
2014-10-11 00:46:38 +00:00
|
|
|
for {
|
2014-11-21 00:01:42 +00:00
|
|
|
try++
|
2014-10-21 21:14:35 +00:00
|
|
|
_, err := recorder.Create(event)
|
2014-10-11 00:46:38 +00:00
|
|
|
if err == nil {
|
|
|
|
break
|
|
|
|
}
|
2014-11-21 00:01:42 +00:00
|
|
|
// If we can't contact the server, then hold everything while we keep trying.
|
|
|
|
// Otherwise, something about the event is malformed and we should abandon it.
|
|
|
|
giveUp := false
|
|
|
|
switch err.(type) {
|
|
|
|
case *client.RequestConstructionError:
|
|
|
|
// We will construct the request the same next time, so don't keep trying.
|
|
|
|
giveUp = true
|
|
|
|
case *errors.StatusError:
|
|
|
|
// This indicates that the server understood and rejected our request.
|
|
|
|
giveUp = true
|
|
|
|
case *errors.UnexpectedObjectError:
|
|
|
|
// We don't expect this; it implies the server's response didn't match a
|
|
|
|
// known pattern. Go ahead and retry.
|
|
|
|
default:
|
|
|
|
// This case includes actual http transport errors. Go ahead and retry.
|
|
|
|
}
|
|
|
|
if giveUp {
|
|
|
|
glog.Errorf("Unable to write event '%#v': '%v' (will not retry!)", event, err)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if try >= 3 {
|
|
|
|
glog.Errorf("Unable to write event '%#v': '%v' (retry limit exceeded!)", event, err)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
glog.Errorf("Unable to write event: '%v' (will retry in 1 second)", err)
|
|
|
|
time.Sleep(1 * time.Second)
|
2014-10-11 00:46:38 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// StartLogging just logs local events, using the given logging function. The
|
|
|
|
// return value can be ignored or used to stop logging, if desired.
|
|
|
|
func StartLogging(logf func(format string, args ...interface{})) watch.Interface {
|
|
|
|
return GetEvents(func(e *api.Event) {
|
2014-12-12 21:27:25 +00:00
|
|
|
logf("Event(%#v): status: '%v', reason: '%v' %v", e.InvolvedObject, e.Condition, e.Reason, e.Message)
|
2014-10-11 00:46:38 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetEvents lets you see *local* events. Convenience function for testing. The
|
|
|
|
// return value can be ignored or used to stop logging, if desired.
|
|
|
|
func GetEvents(f func(*api.Event)) watch.Interface {
|
|
|
|
w := events.Watch()
|
|
|
|
go func() {
|
|
|
|
defer util.HandleCrash()
|
|
|
|
for {
|
|
|
|
watchEvent, open := <-w.ResultChan()
|
|
|
|
if !open {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
event, ok := watchEvent.Object.(*api.Event)
|
|
|
|
if !ok {
|
|
|
|
// This is all local, so there's no reason this should
|
|
|
|
// ever happen.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
f(event)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
|
|
|
const queueLen = 1000
|
|
|
|
|
2014-12-04 08:30:51 +00:00
|
|
|
var events = watch.NewBroadcaster(queueLen)
|
2014-10-11 00:46:38 +00:00
|
|
|
|
|
|
|
// Event constructs an event from the given information and puts it in the queue for sending.
|
2014-11-04 00:06:36 +00:00
|
|
|
// 'object' is the object this event is about. Event will make a reference-- or you may also
|
|
|
|
// pass a reference to the object directly.
|
2014-12-12 21:27:25 +00:00
|
|
|
// 'condition' is the new condition of the object. 'reason' is the reason it now has this status.
|
|
|
|
// Both 'condition' and 'reason' should be short and unique; they will be used to automate
|
2014-10-11 00:46:38 +00:00
|
|
|
// handling of events, so imagine people writing switch statements to handle them. You want to
|
|
|
|
// make that easy.
|
|
|
|
// 'message' is intended to be human readable.
|
2014-10-30 00:27:11 +00:00
|
|
|
//
|
|
|
|
// The resulting event will be created in the same namespace as the reference object.
|
2014-12-12 21:27:25 +00:00
|
|
|
func Event(object runtime.Object, condition, reason, message string) {
|
2014-10-11 00:46:38 +00:00
|
|
|
ref, err := api.GetReference(object)
|
|
|
|
if err != nil {
|
2014-12-12 21:27:25 +00:00
|
|
|
glog.Errorf("Could not construct reference to: '%#v' due to: '%v'. Will not report event: '%v' '%v' '%v'", object, err, condition, reason, message)
|
2014-10-11 00:46:38 +00:00
|
|
|
return
|
|
|
|
}
|
2014-10-30 00:27:11 +00:00
|
|
|
t := util.Now()
|
|
|
|
|
2014-10-11 00:46:38 +00:00
|
|
|
e := &api.Event{
|
2014-10-30 00:27:11 +00:00
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: fmt.Sprintf("%v.%x", ref.Name, t.UnixNano()),
|
|
|
|
Namespace: ref.Namespace,
|
|
|
|
},
|
2014-10-11 00:46:38 +00:00
|
|
|
InvolvedObject: *ref,
|
2014-12-12 21:27:25 +00:00
|
|
|
Condition: condition,
|
2014-10-11 00:46:38 +00:00
|
|
|
Reason: reason,
|
|
|
|
Message: message,
|
2014-10-30 00:27:11 +00:00
|
|
|
Timestamp: t,
|
2014-10-11 00:46:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
events.Action(watch.Added, e)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Eventf is just like Event, but with Sprintf for the message field.
|
2014-11-04 00:06:36 +00:00
|
|
|
func Eventf(object runtime.Object, status, reason, messageFmt string, args ...interface{}) {
|
|
|
|
Event(object, status, reason, fmt.Sprintf(messageFmt, args...))
|
2014-10-11 00:46:38 +00:00
|
|
|
}
|