github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/watch/iowatcher.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors All rights reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package watch
    18  
    19  import (
    20  	"io"
    21  	"sync"
    22  
    23  	"github.com/golang/glog"
    24  	"k8s.io/kubernetes/pkg/runtime"
    25  	"k8s.io/kubernetes/pkg/util"
    26  )
    27  
    28  // Decoder allows StreamWatcher to watch any stream for which a Decoder can be written.
    29  type Decoder interface {
    30  	// Decode should return the type of event, the decoded object, or an error.
    31  	// An error will cause StreamWatcher to call Close(). Decode should block until
    32  	// it has data or an error occurs.
    33  	Decode() (action EventType, object runtime.Object, err error)
    34  
    35  	// Close should close the underlying io.Reader, signalling to the source of
    36  	// the stream that it is no longer being watched. Close() must cause any
    37  	// outstanding call to Decode() to return with an error of some sort.
    38  	Close()
    39  }
    40  
    41  // StreamWatcher turns any stream for which you can write a Decoder interface
    42  // into a watch.Interface.
    43  type StreamWatcher struct {
    44  	source Decoder
    45  	result chan Event
    46  	sync.Mutex
    47  	stopped bool
    48  }
    49  
    50  // NewStreamWatcher creates a StreamWatcher from the given decoder.
    51  func NewStreamWatcher(d Decoder) *StreamWatcher {
    52  	sw := &StreamWatcher{
    53  		source: d,
    54  		// It's easy for a consumer to add buffering via an extra
    55  		// goroutine/channel, but impossible for them to remove it,
    56  		// so nonbuffered is better.
    57  		result: make(chan Event),
    58  	}
    59  	go sw.receive()
    60  	return sw
    61  }
    62  
    63  // ResultChan implements Interface.
    64  func (sw *StreamWatcher) ResultChan() <-chan Event {
    65  	return sw.result
    66  }
    67  
    68  // Stop implements Interface.
    69  func (sw *StreamWatcher) Stop() {
    70  	// Call Close() exactly once by locking and setting a flag.
    71  	sw.Lock()
    72  	defer sw.Unlock()
    73  	if !sw.stopped {
    74  		sw.stopped = true
    75  		sw.source.Close()
    76  	}
    77  }
    78  
    79  // stopping returns true if Stop() was called previously.
    80  func (sw *StreamWatcher) stopping() bool {
    81  	sw.Lock()
    82  	defer sw.Unlock()
    83  	return sw.stopped
    84  }
    85  
    86  // receive reads result from the decoder in a loop and sends down the result channel.
    87  func (sw *StreamWatcher) receive() {
    88  	defer close(sw.result)
    89  	defer sw.Stop()
    90  	defer util.HandleCrash()
    91  	for {
    92  		action, obj, err := sw.source.Decode()
    93  		if err != nil {
    94  			// Ignore expected error.
    95  			if sw.stopping() {
    96  				return
    97  			}
    98  			switch err {
    99  			case io.EOF:
   100  				// watch closed normally
   101  			case io.ErrUnexpectedEOF:
   102  				glog.V(1).Infof("Unexpected EOF during watch stream event decoding: %v", err)
   103  			default:
   104  				msg := "Unable to decode an event from the watch stream: %v"
   105  				if util.IsProbableEOF(err) {
   106  					glog.V(5).Infof(msg, err)
   107  				} else {
   108  					glog.Errorf(msg, err)
   109  				}
   110  			}
   111  			return
   112  		}
   113  		sw.result <- Event{
   114  			Type:   action,
   115  			Object: obj,
   116  		}
   117  	}
   118  }