forked from LaconicNetwork/kompose
update vendored dependencies
This commit is contained in:
+64
@@ -0,0 +1,64 @@
|
||||
package logrus
|
||||
|
||||
// The following code was sourced and modified from the
|
||||
// https://bitbucket.org/tebeka/atexit package governed by the following license:
|
||||
//
|
||||
// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var handlers = []func(){}
|
||||
|
||||
func runHandler(handler func()) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
handler()
|
||||
}
|
||||
|
||||
func runHandlers() {
|
||||
for _, handler := range handlers {
|
||||
runHandler(handler)
|
||||
}
|
||||
}
|
||||
|
||||
// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
|
||||
func Exit(code int) {
|
||||
runHandlers()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
|
||||
// all handlers. The handlers will also be invoked when any Fatal log entry is
|
||||
// made.
|
||||
//
|
||||
// This method is useful when a caller wishes to use logrus to log a fatal
|
||||
// message but also needs to gracefully shutdown. An example usecase could be
|
||||
// closing database connections, or sending a alert that the application is
|
||||
// closing.
|
||||
func RegisterExitHandler(handler func()) {
|
||||
handlers = append(handlers, handler)
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
|
||||
|
||||
|
||||
The simplest way to use Logrus is simply the package-level exported logger:
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.WithFields(log.Fields{
|
||||
"animal": "walrus",
|
||||
"number": 1,
|
||||
"size": 10,
|
||||
}).Info("A walrus appears")
|
||||
}
|
||||
|
||||
Output:
|
||||
time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
|
||||
|
||||
For a full guide visit https://github.com/Sirupsen/logrus
|
||||
*/
|
||||
package logrus
|
||||
+47
-24
@@ -3,11 +3,24 @@ package logrus
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var bufferPool *sync.Pool
|
||||
|
||||
func init() {
|
||||
bufferPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Defines the key when adding errors using WithError.
|
||||
var ErrorKey = "error"
|
||||
|
||||
// An entry is the final or intermediate Logrus logging entry. It contains all
|
||||
// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
|
||||
// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
|
||||
@@ -26,6 +39,9 @@ type Entry struct {
|
||||
|
||||
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
|
||||
Message string
|
||||
|
||||
// When formatter is called in entry.log(), an Buffer may be set to entry
|
||||
Buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
func NewEntry(logger *Logger) *Entry {
|
||||
@@ -36,21 +52,20 @@ func NewEntry(logger *Logger) *Entry {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a reader for the entry, which is a proxy to the formatter.
|
||||
func (entry *Entry) Reader() (*bytes.Buffer, error) {
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
return bytes.NewBuffer(serialized), err
|
||||
}
|
||||
|
||||
// Returns the string representation from the reader and ultimately the
|
||||
// formatter.
|
||||
func (entry *Entry) String() (string, error) {
|
||||
reader, err := entry.Reader()
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
str := string(serialized)
|
||||
return str, nil
|
||||
}
|
||||
|
||||
return reader.String(), err
|
||||
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
||||
func (entry *Entry) WithError(err error) *Entry {
|
||||
return entry.WithField(ErrorKey, err)
|
||||
}
|
||||
|
||||
// Add a single field to the Entry.
|
||||
@@ -60,7 +75,7 @@ func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
||||
|
||||
// Add a map of fields to the Entry.
|
||||
func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||
data := Fields{}
|
||||
data := make(Fields, len(entry.Data)+len(fields))
|
||||
for k, v := range entry.Data {
|
||||
data[k] = v
|
||||
}
|
||||
@@ -70,37 +85,43 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||
return &Entry{Logger: entry.Logger, Data: data}
|
||||
}
|
||||
|
||||
func (entry *Entry) log(level Level, msg string) {
|
||||
// This function is not declared with a pointer value because otherwise
|
||||
// race conditions will occur when using multiple goroutines
|
||||
func (entry Entry) log(level Level, msg string) {
|
||||
var buffer *bytes.Buffer
|
||||
entry.Time = time.Now()
|
||||
entry.Level = level
|
||||
entry.Message = msg
|
||||
|
||||
if err := entry.Logger.Hooks.Fire(level, entry); err != nil {
|
||||
if err := entry.Logger.Hooks.Fire(level, &entry); err != nil {
|
||||
entry.Logger.mu.Lock()
|
||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
|
||||
reader, err := entry.Reader()
|
||||
buffer = bufferPool.Get().(*bytes.Buffer)
|
||||
buffer.Reset()
|
||||
defer bufferPool.Put(buffer)
|
||||
entry.Buffer = buffer
|
||||
serialized, err := entry.Logger.Formatter.Format(&entry)
|
||||
entry.Buffer = nil
|
||||
if err != nil {
|
||||
entry.Logger.mu.Lock()
|
||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
|
||||
entry.Logger.mu.Lock()
|
||||
defer entry.Logger.mu.Unlock()
|
||||
|
||||
_, err = io.Copy(entry.Logger.Out, reader)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||
} else {
|
||||
entry.Logger.mu.Lock()
|
||||
_, err = entry.Logger.Out.Write(serialized)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||
}
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
|
||||
// To avoid Entry#log() returning a value that only would make sense for
|
||||
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
||||
// directly here.
|
||||
if level <= PanicLevel {
|
||||
panic(entry)
|
||||
panic(&entry)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +161,7 @@ func (entry *Entry) Fatal(args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.log(FatalLevel, fmt.Sprint(args...))
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panic(args ...interface{}) {
|
||||
@@ -188,6 +209,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(fmt.Sprintf(format, args...))
|
||||
}
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||
@@ -234,6 +256,7 @@ func (entry *Entry) Fatalln(args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(entry.sprintlnn(args...))
|
||||
}
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicln(args ...interface{}) {
|
||||
|
||||
+5
@@ -48,6 +48,11 @@ func AddHook(hook Hook) {
|
||||
std.Hooks.Add(hook)
|
||||
}
|
||||
|
||||
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
|
||||
func WithError(err error) *Entry {
|
||||
return std.WithField(ErrorKey, err)
|
||||
}
|
||||
|
||||
// WithField creates an entry from the standard logger and adds a field to
|
||||
// it. If you want multiple fields, use `WithFields`.
|
||||
//
|
||||
|
||||
+6
-9
@@ -31,18 +31,15 @@ type Formatter interface {
|
||||
// It's not exported because it's still using Data in an opinionated way. It's to
|
||||
// avoid code duplication between the two default formatters.
|
||||
func prefixFieldClashes(data Fields) {
|
||||
_, ok := data["time"]
|
||||
if ok {
|
||||
data["fields.time"] = data["time"]
|
||||
if t, ok := data["time"]; ok {
|
||||
data["fields.time"] = t
|
||||
}
|
||||
|
||||
_, ok = data["msg"]
|
||||
if ok {
|
||||
data["fields.msg"] = data["msg"]
|
||||
if m, ok := data["msg"]; ok {
|
||||
data["fields.msg"] = m
|
||||
}
|
||||
|
||||
_, ok = data["level"]
|
||||
if ok {
|
||||
data["fields.level"] = data["level"]
|
||||
if l, ok := data["level"]; ok {
|
||||
data["fields.level"] = l
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -11,11 +11,11 @@ type Hook interface {
|
||||
}
|
||||
|
||||
// Internal type for storing the hooks on a logger instance.
|
||||
type levelHooks map[Level][]Hook
|
||||
type LevelHooks map[Level][]Hook
|
||||
|
||||
// Add a hook to an instance of logger. This is called with
|
||||
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
|
||||
func (hooks levelHooks) Add(hook Hook) {
|
||||
func (hooks LevelHooks) Add(hook Hook) {
|
||||
for _, level := range hook.Levels() {
|
||||
hooks[level] = append(hooks[level], hook)
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func (hooks levelHooks) Add(hook Hook) {
|
||||
|
||||
// Fire all the hooks for the passed level. Used by `entry.log` to fire
|
||||
// appropriate hooks for a log entry.
|
||||
func (hooks levelHooks) Fire(level Level, entry *Entry) error {
|
||||
func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
|
||||
for _, hook := range hooks[level] {
|
||||
if err := hook.Fire(entry); err != nil {
|
||||
return err
|
||||
|
||||
+39
-5
@@ -5,9 +5,40 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type fieldKey string
|
||||
type FieldMap map[fieldKey]string
|
||||
|
||||
const (
|
||||
FieldKeyMsg = "msg"
|
||||
FieldKeyLevel = "level"
|
||||
FieldKeyTime = "time"
|
||||
)
|
||||
|
||||
func (f FieldMap) resolve(key fieldKey) string {
|
||||
if k, ok := f[key]; ok {
|
||||
return k
|
||||
}
|
||||
|
||||
return string(key)
|
||||
}
|
||||
|
||||
type JSONFormatter struct {
|
||||
// TimestampFormat sets the format used for marshaling timestamps.
|
||||
TimestampFormat string
|
||||
|
||||
// DisableTimestamp allows disabling automatic timestamps in output
|
||||
DisableTimestamp bool
|
||||
|
||||
// FieldMap allows users to customize the names of keys for various fields.
|
||||
// As an example:
|
||||
// formatter := &JSONFormatter{
|
||||
// FieldMap: FieldMap{
|
||||
// FieldKeyTime: "@timestamp",
|
||||
// FieldKeyLevel: "@level",
|
||||
// FieldKeyLevel: "@message",
|
||||
// },
|
||||
// }
|
||||
FieldMap FieldMap
|
||||
}
|
||||
|
||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
@@ -24,13 +55,16 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
}
|
||||
prefixFieldClashes(data)
|
||||
|
||||
if f.TimestampFormat == "" {
|
||||
f.TimestampFormat = DefaultTimestampFormat
|
||||
timestampFormat := f.TimestampFormat
|
||||
if timestampFormat == "" {
|
||||
timestampFormat = DefaultTimestampFormat
|
||||
}
|
||||
|
||||
data["time"] = entry.Time.Format(f.TimestampFormat)
|
||||
data["msg"] = entry.Message
|
||||
data["level"] = entry.Level.String()
|
||||
if !f.DisableTimestamp {
|
||||
data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
|
||||
}
|
||||
data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
|
||||
data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
|
||||
|
||||
serialized, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
|
||||
+139
-34
@@ -8,13 +8,13 @@ import (
|
||||
|
||||
type Logger struct {
|
||||
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
|
||||
// file, or leave it default which is `os.Stdout`. You can also set this to
|
||||
// file, or leave it default which is `os.Stderr`. You can also set this to
|
||||
// something more adventorous, such as logging to Kafka.
|
||||
Out io.Writer
|
||||
// Hooks for the logger instance. These allow firing events based on logging
|
||||
// levels and log entries. For example, to send errors to an error tracking
|
||||
// service, log to StatsD or dump the core on fatal errors.
|
||||
Hooks levelHooks
|
||||
Hooks LevelHooks
|
||||
// All log entries pass through the formatter before logged to Out. The
|
||||
// included formatters are `TextFormatter` and `JSONFormatter` for which
|
||||
// TextFormatter is the default. In development (when a TTY is attached) it
|
||||
@@ -26,8 +26,31 @@ type Logger struct {
|
||||
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
||||
// logged. `logrus.Debug` is useful in
|
||||
Level Level
|
||||
// Used to sync writing to the log.
|
||||
mu sync.Mutex
|
||||
// Used to sync writing to the log. Locking is enabled by Default
|
||||
mu MutexWrap
|
||||
// Reusable empty entry
|
||||
entryPool sync.Pool
|
||||
}
|
||||
|
||||
type MutexWrap struct {
|
||||
lock sync.Mutex
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Lock() {
|
||||
if !mw.disabled {
|
||||
mw.lock.Lock()
|
||||
}
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Unlock() {
|
||||
if !mw.disabled {
|
||||
mw.lock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Disable() {
|
||||
mw.disabled = true
|
||||
}
|
||||
|
||||
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
||||
@@ -37,7 +60,7 @@ type Logger struct {
|
||||
// var log = &Logger{
|
||||
// Out: os.Stderr,
|
||||
// Formatter: new(JSONFormatter),
|
||||
// Hooks: make(levelHooks),
|
||||
// Hooks: make(LevelHooks),
|
||||
// Level: logrus.DebugLevel,
|
||||
// }
|
||||
//
|
||||
@@ -46,158 +69,240 @@ func New() *Logger {
|
||||
return &Logger{
|
||||
Out: os.Stderr,
|
||||
Formatter: new(TextFormatter),
|
||||
Hooks: make(levelHooks),
|
||||
Hooks: make(LevelHooks),
|
||||
Level: InfoLevel,
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a field to the log entry, note that you it doesn't log until you call
|
||||
func (logger *Logger) newEntry() *Entry {
|
||||
entry, ok := logger.entryPool.Get().(*Entry)
|
||||
if ok {
|
||||
return entry
|
||||
}
|
||||
return NewEntry(logger)
|
||||
}
|
||||
|
||||
func (logger *Logger) releaseEntry(entry *Entry) {
|
||||
logger.entryPool.Put(entry)
|
||||
}
|
||||
|
||||
// Adds a field to the log entry, note that it doesn't log until you call
|
||||
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
|
||||
// Ff you want multiple fields, use `WithFields`.
|
||||
// If you want multiple fields, use `WithFields`.
|
||||
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
||||
return NewEntry(logger).WithField(key, value)
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithField(key, value)
|
||||
}
|
||||
|
||||
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
||||
// each `Field`.
|
||||
func (logger *Logger) WithFields(fields Fields) *Entry {
|
||||
return NewEntry(logger).WithFields(fields)
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithFields(fields)
|
||||
}
|
||||
|
||||
// Add an error as single field to the log entry. All it does is call
|
||||
// `WithError` for the given `error`.
|
||||
func (logger *Logger) WithError(err error) *Entry {
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithError(err)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
NewEntry(logger).Debugf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Debugf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
NewEntry(logger).Infof(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Infof(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||
NewEntry(logger).Printf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Printf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
NewEntry(logger).Errorf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Errorf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
NewEntry(logger).Fatalf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Fatalf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
NewEntry(logger).Panicf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Panicf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Debug(args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
NewEntry(logger).Debug(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Debug(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Info(args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
NewEntry(logger).Info(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Info(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Print(args ...interface{}) {
|
||||
NewEntry(logger).Info(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Info(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warn(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warn(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warn(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warning(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warn(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warn(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Error(args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
NewEntry(logger).Error(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Error(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatal(args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
NewEntry(logger).Fatal(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Fatal(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panic(args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
NewEntry(logger).Panic(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Panic(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugln(args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
NewEntry(logger).Debugln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Debugln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Infoln(args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
NewEntry(logger).Infoln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Infoln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Println(args ...interface{}) {
|
||||
NewEntry(logger).Println(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Println(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnln(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningln(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorln(args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
NewEntry(logger).Errorln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Errorln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
NewEntry(logger).Fatalln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Fatalln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicln(args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
NewEntry(logger).Panicln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Panicln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
//When file is opened with appending mode, it's safe to
|
||||
//write concurrently to a file (within 4k message on Linux).
|
||||
//In these cases user can choose to disable the lock.
|
||||
func (logger *Logger) SetNoLock() {
|
||||
logger.mu.Disable()
|
||||
}
|
||||
|
||||
+51
-2
@@ -3,6 +3,7 @@ package logrus
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Fields type, used to pass to `WithFields`.
|
||||
@@ -33,7 +34,7 @@ func (level Level) String() string {
|
||||
|
||||
// ParseLevel takes a string level and returns the Logrus log level constant.
|
||||
func ParseLevel(lvl string) (Level, error) {
|
||||
switch lvl {
|
||||
switch strings.ToLower(lvl) {
|
||||
case "panic":
|
||||
return PanicLevel, nil
|
||||
case "fatal":
|
||||
@@ -52,6 +53,16 @@ func ParseLevel(lvl string) (Level, error) {
|
||||
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
|
||||
}
|
||||
|
||||
// A constant exposing all logging levels
|
||||
var AllLevels = []Level{
|
||||
PanicLevel,
|
||||
FatalLevel,
|
||||
ErrorLevel,
|
||||
WarnLevel,
|
||||
InfoLevel,
|
||||
DebugLevel,
|
||||
}
|
||||
|
||||
// These are the different logging levels. You can set the logging level to log
|
||||
// on your instance of logger, obtained with `logrus.New()`.
|
||||
const (
|
||||
@@ -74,7 +85,11 @@ const (
|
||||
)
|
||||
|
||||
// Won't compile if StdLogger can't be realized by a log.Logger
|
||||
var _ StdLogger = &log.Logger{}
|
||||
var (
|
||||
_ StdLogger = &log.Logger{}
|
||||
_ StdLogger = &Entry{}
|
||||
_ StdLogger = &Logger{}
|
||||
)
|
||||
|
||||
// StdLogger is what your logrus-enabled library should take, that way
|
||||
// it'll accept a stdlib logger and a logrus logger. There's no standard
|
||||
@@ -92,3 +107,37 @@ type StdLogger interface {
|
||||
Panicf(string, ...interface{})
|
||||
Panicln(...interface{})
|
||||
}
|
||||
|
||||
// The FieldLogger interface generalizes the Entry and Logger types
|
||||
type FieldLogger interface {
|
||||
WithField(key string, value interface{}) *Entry
|
||||
WithFields(fields Fields) *Entry
|
||||
WithError(err error) *Entry
|
||||
|
||||
Debugf(format string, args ...interface{})
|
||||
Infof(format string, args ...interface{})
|
||||
Printf(format string, args ...interface{})
|
||||
Warnf(format string, args ...interface{})
|
||||
Warningf(format string, args ...interface{})
|
||||
Errorf(format string, args ...interface{})
|
||||
Fatalf(format string, args ...interface{})
|
||||
Panicf(format string, args ...interface{})
|
||||
|
||||
Debug(args ...interface{})
|
||||
Info(args ...interface{})
|
||||
Print(args ...interface{})
|
||||
Warn(args ...interface{})
|
||||
Warning(args ...interface{})
|
||||
Error(args ...interface{})
|
||||
Fatal(args ...interface{})
|
||||
Panic(args ...interface{})
|
||||
|
||||
Debugln(args ...interface{})
|
||||
Infoln(args ...interface{})
|
||||
Println(args ...interface{})
|
||||
Warnln(args ...interface{})
|
||||
Warningln(args ...interface{})
|
||||
Errorln(args ...interface{})
|
||||
Fatalln(args ...interface{})
|
||||
Panicln(args ...interface{})
|
||||
}
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// +build appengine
|
||||
|
||||
package logrus
|
||||
|
||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||
func IsTerminal() bool {
|
||||
return true
|
||||
}
|
||||
Generated
Vendored
+3
@@ -1,3 +1,6 @@
|
||||
// +build darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import "syscall"
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
// Based on ssh/terminal:
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package logrus
|
||||
|
||||
import "syscall"
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
||||
|
||||
type Termios syscall.Termios
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin.
|
||||
*/
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
Cflag uint32
|
||||
Lflag uint32
|
||||
Cc [20]uint8
|
||||
Ispeed uint32
|
||||
Ospeed uint32
|
||||
}
|
||||
+2
@@ -3,6 +3,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import "syscall"
|
||||
|
||||
+4
-3
@@ -3,7 +3,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux darwin freebsd openbsd
|
||||
// +build linux darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
@@ -12,9 +13,9 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||
func IsTerminal() bool {
|
||||
fd := syscall.Stdout
|
||||
fd := syscall.Stderr
|
||||
var termios Termios
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// +build solaris,!appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal() bool {
|
||||
_, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA)
|
||||
return err == nil
|
||||
}
|
||||
+3
-3
@@ -3,7 +3,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
// +build windows,!appengine
|
||||
|
||||
package logrus
|
||||
|
||||
@@ -18,9 +18,9 @@ var (
|
||||
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||
func IsTerminal() bool {
|
||||
fd := syscall.Stdout
|
||||
fd := syscall.Stderr
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
|
||||
+41
-22
@@ -3,6 +3,7 @@ package logrus
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -56,6 +57,7 @@ type TextFormatter struct {
|
||||
}
|
||||
|
||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
var b *bytes.Buffer
|
||||
var keys []string = make([]string, 0, len(entry.Data))
|
||||
for k := range entry.Data {
|
||||
keys = append(keys, k)
|
||||
@@ -64,24 +66,31 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
if !f.DisableSorting {
|
||||
sort.Strings(keys)
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
if entry.Buffer != nil {
|
||||
b = entry.Buffer
|
||||
} else {
|
||||
b = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
prefixFieldClashes(entry.Data)
|
||||
|
||||
isColored := (f.ForceColors || isTerminal) && !f.DisableColors
|
||||
isColorTerminal := isTerminal && (runtime.GOOS != "windows")
|
||||
isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors
|
||||
|
||||
if f.TimestampFormat == "" {
|
||||
f.TimestampFormat = DefaultTimestampFormat
|
||||
timestampFormat := f.TimestampFormat
|
||||
if timestampFormat == "" {
|
||||
timestampFormat = DefaultTimestampFormat
|
||||
}
|
||||
if isColored {
|
||||
f.printColored(b, entry, keys)
|
||||
f.printColored(b, entry, keys, timestampFormat)
|
||||
} else {
|
||||
if !f.DisableTimestamp {
|
||||
f.appendKeyValue(b, "time", entry.Time.Format(f.TimestampFormat))
|
||||
f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
|
||||
}
|
||||
f.appendKeyValue(b, "level", entry.Level.String())
|
||||
f.appendKeyValue(b, "msg", entry.Message)
|
||||
if entry.Message != "" {
|
||||
f.appendKeyValue(b, "msg", entry.Message)
|
||||
}
|
||||
for _, key := range keys {
|
||||
f.appendKeyValue(b, key, entry.Data[key])
|
||||
}
|
||||
@@ -91,7 +100,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string) {
|
||||
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
|
||||
var levelColor int
|
||||
switch entry.Level {
|
||||
case DebugLevel:
|
||||
@@ -109,11 +118,12 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
|
||||
if !f.FullTimestamp {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
|
||||
} else {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(f.TimestampFormat), entry.Message)
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
|
||||
}
|
||||
for _, k := range keys {
|
||||
v := entry.Data[k]
|
||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v)
|
||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
|
||||
f.appendValue(b, v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,27 +133,36 @@ func needsQuoting(text string) bool {
|
||||
(ch >= 'A' && ch <= 'Z') ||
|
||||
(ch >= '0' && ch <= '9') ||
|
||||
ch == '-' || ch == '.') {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) {
|
||||
switch value.(type) {
|
||||
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
||||
|
||||
b.WriteString(key)
|
||||
b.WriteByte('=')
|
||||
f.appendValue(b, value)
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
|
||||
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if needsQuoting(value.(string)) {
|
||||
fmt.Fprintf(b, "%v=%s ", key, value)
|
||||
if !needsQuoting(value) {
|
||||
b.WriteString(value)
|
||||
} else {
|
||||
fmt.Fprintf(b, "%v=%q ", key, value)
|
||||
fmt.Fprintf(b, "%q", value)
|
||||
}
|
||||
case error:
|
||||
if needsQuoting(value.(error).Error()) {
|
||||
fmt.Fprintf(b, "%v=%s ", key, value)
|
||||
errmsg := value.Error()
|
||||
if !needsQuoting(errmsg) {
|
||||
b.WriteString(errmsg)
|
||||
} else {
|
||||
fmt.Fprintf(b, "%v=%q ", key, value)
|
||||
fmt.Fprintf(b, "%q", errmsg)
|
||||
}
|
||||
default:
|
||||
fmt.Fprintf(b, "%v=%v ", key, value)
|
||||
fmt.Fprint(b, value)
|
||||
}
|
||||
}
|
||||
|
||||
+25
-3
@@ -7,18 +7,40 @@ import (
|
||||
)
|
||||
|
||||
func (logger *Logger) Writer() *io.PipeWriter {
|
||||
return logger.WriterLevel(InfoLevel)
|
||||
}
|
||||
|
||||
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
||||
reader, writer := io.Pipe()
|
||||
|
||||
go logger.writerScanner(reader)
|
||||
var printFunc func(args ...interface{})
|
||||
switch level {
|
||||
case DebugLevel:
|
||||
printFunc = logger.Debug
|
||||
case InfoLevel:
|
||||
printFunc = logger.Info
|
||||
case WarnLevel:
|
||||
printFunc = logger.Warn
|
||||
case ErrorLevel:
|
||||
printFunc = logger.Error
|
||||
case FatalLevel:
|
||||
printFunc = logger.Fatal
|
||||
case PanicLevel:
|
||||
printFunc = logger.Panic
|
||||
default:
|
||||
printFunc = logger.Print
|
||||
}
|
||||
|
||||
go logger.writerScanner(reader, printFunc)
|
||||
runtime.SetFinalizer(writer, writerFinalizer)
|
||||
|
||||
return writer
|
||||
}
|
||||
|
||||
func (logger *Logger) writerScanner(reader *io.PipeReader) {
|
||||
func (logger *Logger) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
for scanner.Scan() {
|
||||
logger.Print(scanner.Text())
|
||||
printFunc(scanner.Text())
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
logger.Errorf("Error while reading from Writer: %s", err)
|
||||
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
kerrors "k8s.io/kubernetes/pkg/api/errors"
|
||||
kclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
kutil "k8s.io/kubernetes/pkg/util"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
"github.com/openshift/origin/pkg/deploy/util"
|
||||
)
|
||||
|
||||
// NewDeploymentConfigReaper returns a new reaper for deploymentConfigs
|
||||
func NewDeploymentConfigReaper(oc client.Interface, kc kclient.Interface) kubectl.Reaper {
|
||||
return &DeploymentConfigReaper{oc: oc, kc: kc, pollInterval: kubectl.Interval, timeout: kubectl.Timeout}
|
||||
}
|
||||
|
||||
// DeploymentConfigReaper implements the Reaper interface for deploymentConfigs
|
||||
type DeploymentConfigReaper struct {
|
||||
oc client.Interface
|
||||
kc kclient.Interface
|
||||
pollInterval, timeout time.Duration
|
||||
}
|
||||
|
||||
// pause marks the deployment configuration as paused to avoid triggering new
|
||||
// deployments.
|
||||
func (reaper *DeploymentConfigReaper) pause(namespace, name string) (*deployapi.DeploymentConfig, error) {
|
||||
return client.UpdateConfigWithRetries(reaper.oc, namespace, name, func(d *deployapi.DeploymentConfig) {
|
||||
d.Spec.RevisionHistoryLimit = kutil.Int32Ptr(0)
|
||||
d.Spec.Replicas = 0
|
||||
d.Spec.Paused = true
|
||||
})
|
||||
}
|
||||
|
||||
// Stop scales a replication controller via its deployment configuration down to
|
||||
// zero replicas, waits for all of them to get deleted and then deletes both the
|
||||
// replication controller and its deployment configuration.
|
||||
func (reaper *DeploymentConfigReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *kapi.DeleteOptions) error {
|
||||
// Pause the deployment configuration to prevent the new deployments from
|
||||
// being triggered.
|
||||
config, err := reaper.pause(namespace, name)
|
||||
configNotFound := kerrors.IsNotFound(err)
|
||||
if err != nil && !configNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
isPaused bool
|
||||
legacy bool
|
||||
)
|
||||
// Determine if the deployment config controller noticed the pause.
|
||||
if !configNotFound {
|
||||
if err := wait.Poll(1*time.Second, 1*time.Minute, func() (bool, error) {
|
||||
dc, err := reaper.oc.DeploymentConfigs(namespace).Get(name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
isPaused = dc.Spec.Paused
|
||||
return dc.Status.ObservedGeneration >= config.Generation, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we failed to pause the deployment config, it means we are talking to
|
||||
// old API that does not support pausing. In that case, we delete the
|
||||
// deployment config to stay backward compatible.
|
||||
if !isPaused {
|
||||
if err := reaper.oc.DeploymentConfigs(namespace).Delete(name); err != nil {
|
||||
return err
|
||||
}
|
||||
// Setting this to true avoid deleting the config at the end.
|
||||
legacy = true
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up deployments related to the config. Even if the deployment
|
||||
// configuration has been deleted, we want to sweep the existing replication
|
||||
// controllers and clean them up.
|
||||
options := kapi.ListOptions{LabelSelector: util.ConfigSelector(name)}
|
||||
rcList, err := reaper.kc.ReplicationControllers(namespace).List(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rcReaper, err := kubectl.ReaperFor(kapi.Kind("ReplicationController"), reaper.kc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If there is neither a config nor any deployments, nor any deployer pods, we can return NotFound.
|
||||
deployments := rcList.Items
|
||||
|
||||
if configNotFound && len(deployments) == 0 {
|
||||
return kerrors.NewNotFound(kapi.Resource("deploymentconfig"), name)
|
||||
}
|
||||
|
||||
for _, rc := range deployments {
|
||||
if err = rcReaper.Stop(rc.Namespace, rc.Name, timeout, gracePeriod); err != nil {
|
||||
// Better not error out here...
|
||||
glog.Infof("Cannot delete ReplicationController %s/%s for deployment config %s/%s: %v", rc.Namespace, rc.Name, namespace, name, err)
|
||||
}
|
||||
|
||||
// Only remove deployer pods when the deployment was failed. For completed
|
||||
// deployment the pods should be already deleted.
|
||||
if !util.IsFailedDeployment(&rc) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Delete all deployer and hook pods
|
||||
options = kapi.ListOptions{LabelSelector: util.DeployerPodSelector(rc.Name)}
|
||||
podList, err := reaper.kc.Pods(rc.Namespace).List(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, pod := range podList.Items {
|
||||
err := reaper.kc.Pods(pod.Namespace).Delete(pod.Name, gracePeriod)
|
||||
if err != nil {
|
||||
// Better not error out here...
|
||||
glog.Infof("Cannot delete lifecycle Pod %s/%s for deployment config %s/%s: %v", pod.Namespace, pod.Name, namespace, name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing to delete or we already deleted the deployment config because we
|
||||
// failed to pause.
|
||||
if configNotFound || legacy {
|
||||
return nil
|
||||
}
|
||||
|
||||
return reaper.oc.DeploymentConfigs(namespace).Delete(name)
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// Package cmd contains various interface implementations for command-line tools
|
||||
// associated with deploymentconfigs.
|
||||
package cmd
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
)
|
||||
|
||||
var basic = kubectl.BasicReplicationController{}
|
||||
|
||||
type BasicDeploymentConfigController struct{}
|
||||
|
||||
func (BasicDeploymentConfigController) ParamNames() []kubectl.GeneratorParam {
|
||||
return basic.ParamNames()
|
||||
}
|
||||
|
||||
func (BasicDeploymentConfigController) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
obj, err := basic.Generate(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch t := obj.(type) {
|
||||
case *kapi.ReplicationController:
|
||||
obj = &deployapi.DeploymentConfig{
|
||||
ObjectMeta: t.ObjectMeta,
|
||||
Spec: deployapi.DeploymentConfigSpec{
|
||||
Selector: t.Spec.Selector,
|
||||
Replicas: t.Spec.Replicas,
|
||||
Template: t.Spec.Template,
|
||||
},
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized object type: %v", reflect.TypeOf(t))
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"text/tabwriter"
|
||||
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
kclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
deployutil "github.com/openshift/origin/pkg/deploy/util"
|
||||
)
|
||||
|
||||
func NewDeploymentConfigHistoryViewer(oc client.Interface, kc kclient.Interface) kubectl.HistoryViewer {
|
||||
return &DeploymentConfigHistoryViewer{dn: oc, rn: kc}
|
||||
}
|
||||
|
||||
// DeploymentConfigHistoryViewer is an implementation of the kubectl HistoryViewer interface
|
||||
// for deployment configs.
|
||||
type DeploymentConfigHistoryViewer struct {
|
||||
rn kclient.ReplicationControllersNamespacer
|
||||
dn client.DeploymentConfigsNamespacer
|
||||
}
|
||||
|
||||
var _ kubectl.HistoryViewer = &DeploymentConfigHistoryViewer{}
|
||||
|
||||
// ViewHistory returns a description of all the history it can find for a deployment config.
|
||||
func (h *DeploymentConfigHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
|
||||
opts := kapi.ListOptions{LabelSelector: deployutil.ConfigSelector(name)}
|
||||
deploymentList, err := h.rn.ReplicationControllers(namespace).List(opts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
history := deploymentList.Items
|
||||
|
||||
if len(deploymentList.Items) == 0 {
|
||||
return "No rollout history found.", nil
|
||||
}
|
||||
|
||||
// Print details of a specific revision
|
||||
if revision > 0 {
|
||||
var desired *kapi.PodTemplateSpec
|
||||
// We could use a binary search here but brute-force is always faster to write
|
||||
for i := range history {
|
||||
rc := history[i]
|
||||
|
||||
if deployutil.DeploymentVersionFor(&rc) == revision {
|
||||
desired = rc.Spec.Template
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if desired == nil {
|
||||
return "", fmt.Errorf("unable to find the specified revision")
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
kubectl.DescribePodTemplate(desired, buf)
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
sort.Sort(deployutil.ByLatestVersionAsc(history))
|
||||
|
||||
return tabbedString(func(out *tabwriter.Writer) error {
|
||||
fmt.Fprintf(out, "REVISION\tSTATUS\tCAUSE\n")
|
||||
for i := range history {
|
||||
rc := history[i]
|
||||
|
||||
rev := deployutil.DeploymentVersionFor(&rc)
|
||||
status := deployutil.DeploymentStatusFor(&rc)
|
||||
cause := rc.Annotations[deployapi.DeploymentStatusReasonAnnotation]
|
||||
if len(cause) == 0 {
|
||||
cause = "<unknown>"
|
||||
}
|
||||
fmt.Fprintf(out, "%d\t%s\t%s\n", rev, status, cause)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Re-use from an utility package
|
||||
func tabbedString(f func(*tabwriter.Writer) error) (string, error) {
|
||||
out := new(tabwriter.Writer)
|
||||
buf := &bytes.Buffer{}
|
||||
out.Init(buf, 0, 8, 1, '\t', 0)
|
||||
|
||||
err := f(out)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
out.Flush()
|
||||
str := string(buf.String())
|
||||
return str, nil
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
)
|
||||
|
||||
func NewDeploymentConfigRollbacker(oc client.Interface) kubectl.Rollbacker {
|
||||
return &DeploymentConfigRollbacker{dn: oc}
|
||||
}
|
||||
|
||||
// DeploymentConfigRollbacker is an implementation of the kubectl Rollbacker interface
|
||||
// for deployment configs.
|
||||
type DeploymentConfigRollbacker struct {
|
||||
dn client.DeploymentConfigsNamespacer
|
||||
}
|
||||
|
||||
var _ kubectl.Rollbacker = &DeploymentConfigRollbacker{}
|
||||
|
||||
// Rollback the provided deployment config to a specific revision. If revision is zero, we will
|
||||
// rollback to the previous deployment.
|
||||
func (r *DeploymentConfigRollbacker) Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64) (string, error) {
|
||||
config, ok := obj.(*deployapi.DeploymentConfig)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("passed object is not a deployment config: %#v", obj)
|
||||
}
|
||||
if config.Spec.Paused {
|
||||
return "", fmt.Errorf("cannot rollback a paused config; resume it first with 'rollout resume dc/%s' and try again", config.Name)
|
||||
}
|
||||
|
||||
rollback := &deployapi.DeploymentConfigRollback{
|
||||
Name: config.Name,
|
||||
UpdatedAnnotations: updatedAnnotations,
|
||||
Spec: deployapi.DeploymentConfigRollbackSpec{
|
||||
Revision: toRevision,
|
||||
IncludeTemplate: true,
|
||||
},
|
||||
}
|
||||
|
||||
rolledback, err := r.dn.DeploymentConfigs(config.Namespace).Rollback(rollback)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = r.dn.DeploymentConfigs(config.Namespace).Update(rolledback)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return "rolled back", nil
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
kclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
"github.com/openshift/origin/pkg/deploy/util"
|
||||
)
|
||||
|
||||
// NewDeploymentConfigScaler returns a new scaler for deploymentConfigs
|
||||
func NewDeploymentConfigScaler(oc client.Interface, kc kclient.Interface) kubectl.Scaler {
|
||||
return &DeploymentConfigScaler{rcClient: kc, dcClient: oc, clientInterface: kc}
|
||||
}
|
||||
|
||||
// DeploymentConfigScaler is a wrapper for the kubectl Scaler client
|
||||
type DeploymentConfigScaler struct {
|
||||
rcClient kclient.ReplicationControllersNamespacer
|
||||
dcClient client.DeploymentConfigsNamespacer
|
||||
|
||||
clientInterface kclient.Interface
|
||||
}
|
||||
|
||||
// Scale updates the DeploymentConfig with the provided namespace/name, to a
|
||||
// new size, with optional precondition check (if preconditions is not nil),
|
||||
// optional retries (if retry is not nil), and then optionally waits for its
|
||||
// deployment replica count to reach the new value (if wait is not nil).
|
||||
func (scaler *DeploymentConfigScaler) Scale(namespace, name string, newSize uint, preconditions *kubectl.ScalePrecondition, retry, waitForReplicas *kubectl.RetryParams) error {
|
||||
if preconditions == nil {
|
||||
preconditions = &kubectl.ScalePrecondition{Size: -1, ResourceVersion: ""}
|
||||
}
|
||||
if retry == nil {
|
||||
// Make it try only once, immediately
|
||||
retry = &kubectl.RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond}
|
||||
}
|
||||
cond := kubectl.ScaleCondition(scaler, preconditions, namespace, name, newSize, nil)
|
||||
if err := wait.Poll(retry.Interval, retry.Timeout, cond); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: convert to a watch and use resource version from the ScaleCondition - kubernetes/kubernetes#31051
|
||||
if waitForReplicas != nil {
|
||||
dc, err := scaler.dcClient.DeploymentConfigs(namespace).Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rc, err := scaler.rcClient.ReplicationControllers(namespace).Get(util.LatestDeploymentNameForConfig(dc))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout, controllerHasSpecifiedReplicas(scaler.clientInterface, rc, dc.Spec.Replicas))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ScaleSimple does a simple one-shot attempt at scaling - not useful on its
|
||||
// own, but a necessary building block for Scale.
|
||||
func (scaler *DeploymentConfigScaler) ScaleSimple(namespace, name string, preconditions *kubectl.ScalePrecondition, newSize uint) (string, error) {
|
||||
scale, err := scaler.dcClient.DeploymentConfigs(namespace).GetScale(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
scale.Spec.Replicas = int32(newSize)
|
||||
updated, err := scaler.dcClient.DeploymentConfigs(namespace).UpdateScale(scale)
|
||||
if err != nil {
|
||||
return "", kubectl.ScaleError{FailureType: kubectl.ScaleUpdateFailure, ResourceVersion: "Unknown", ActualError: err}
|
||||
}
|
||||
return updated.ResourceVersion, nil
|
||||
}
|
||||
|
||||
// controllerHasSpecifiedReplicas returns a condition that will be true if and
|
||||
// only if the specified replica count for a controller's ReplicaSelector
|
||||
// equals the Replicas count.
|
||||
//
|
||||
// This is a slightly modified version of
|
||||
// unversioned.ControllerHasDesiredReplicas. This is necessary because when
|
||||
// scaling an RC via a DC, the RC spec replica count is not immediately
|
||||
// updated to match the owning DC.
|
||||
func controllerHasSpecifiedReplicas(c kclient.Interface, controller *kapi.ReplicationController, specifiedReplicas int32) wait.ConditionFunc {
|
||||
// If we're given a controller where the status lags the spec, it either means that the controller is stale,
|
||||
// or that the rc manager hasn't noticed the update yet. Polling status.Replicas is not safe in the latter case.
|
||||
desiredGeneration := controller.Generation
|
||||
|
||||
return func() (bool, error) {
|
||||
ctrl, err := c.ReplicationControllers(controller.Namespace).Get(controller.Name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// There's a chance a concurrent update modifies the Spec.Replicas causing this check to pass,
|
||||
// or, after this check has passed, a modification causes the rc manager to create more pods.
|
||||
// This will not be an issue once we've implemented graceful delete for rcs, but till then
|
||||
// concurrent stop operations on the same rc might have unintended side effects.
|
||||
return ctrl.Status.ObservedGeneration >= desiredGeneration && ctrl.Status.Replicas == specifiedReplicas, nil
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
deployutil "github.com/openshift/origin/pkg/deploy/util"
|
||||
)
|
||||
|
||||
func NewDeploymentConfigStatusViewer(oc client.Interface) kubectl.StatusViewer {
|
||||
return &DeploymentConfigStatusViewer{dn: oc}
|
||||
}
|
||||
|
||||
// DeploymentConfigStatusViewer is an implementation of the kubectl StatusViewer interface
|
||||
// for deployment configs.
|
||||
type DeploymentConfigStatusViewer struct {
|
||||
dn client.DeploymentConfigsNamespacer
|
||||
}
|
||||
|
||||
var _ kubectl.StatusViewer = &DeploymentConfigStatusViewer{}
|
||||
|
||||
// Status returns a message describing deployment status, and a bool value indicating if the status is considered done
|
||||
func (s *DeploymentConfigStatusViewer) Status(namespace, name string, desiredRevision int64) (string, bool, error) {
|
||||
config, err := s.dn.DeploymentConfigs(namespace).Get(name)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
latestRevision := config.Status.LatestVersion
|
||||
|
||||
if latestRevision == 0 {
|
||||
switch {
|
||||
case deployutil.HasImageChangeTrigger(config):
|
||||
return fmt.Sprintf("Deployment config %q waiting on image update\n", name), false, nil
|
||||
|
||||
case len(config.Spec.Triggers) == 0:
|
||||
return "", true, fmt.Errorf("Deployment config %q waiting on manual update (use 'oc rollout latest %s')", name, name)
|
||||
}
|
||||
}
|
||||
|
||||
if desiredRevision > 0 && latestRevision != desiredRevision {
|
||||
return "", false, fmt.Errorf("desired revision (%d) is different from the running revision (%d)", desiredRevision, latestRevision)
|
||||
}
|
||||
|
||||
cond := deployutil.GetDeploymentCondition(config.Status, deployapi.DeploymentProgressing)
|
||||
|
||||
if config.Generation <= config.Status.ObservedGeneration {
|
||||
switch {
|
||||
case cond != nil && cond.Reason == deployutil.NewRcAvailableReason:
|
||||
return fmt.Sprintf("%s\n", cond.Message), true, nil
|
||||
|
||||
case cond != nil && cond.Reason == deployutil.TimedOutReason:
|
||||
return "", true, errors.New(cond.Message)
|
||||
|
||||
case cond != nil && cond.Reason == deployutil.PausedDeployReason:
|
||||
return "", true, fmt.Errorf("Deployment config %q is paused. Resume to continue watching the status of the rollout.\n", config.Name)
|
||||
|
||||
case config.Status.UpdatedReplicas < config.Spec.Replicas:
|
||||
return fmt.Sprintf("Waiting for rollout to finish: %d out of %d new replicas have been updated...\n", config.Status.UpdatedReplicas, config.Spec.Replicas), false, nil
|
||||
|
||||
case config.Status.Replicas > config.Status.UpdatedReplicas:
|
||||
return fmt.Sprintf("Waiting for rollout to finish: %d old replicas are pending termination...\n", config.Status.Replicas-config.Status.UpdatedReplicas), false, nil
|
||||
|
||||
case config.Status.AvailableReplicas < config.Status.UpdatedReplicas:
|
||||
return fmt.Sprintf("Waiting for rollout to finish: %d of %d updated replicas are available...\n", config.Status.AvailableReplicas, config.Status.UpdatedReplicas), false, nil
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("Waiting for latest deployment config spec to be observed by the controller loop...\n"), false, nil
|
||||
}
|
||||
+630
@@ -0,0 +1,630 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
kdeplutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
"github.com/openshift/origin/pkg/util/namer"
|
||||
kclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
)
|
||||
|
||||
const (
|
||||
// Reasons for deployment config conditions:
|
||||
//
|
||||
// ReplicationControllerUpdatedReason is added in a deployment config when one of its replication
|
||||
// controllers is updated as part of the rollout process.
|
||||
ReplicationControllerUpdatedReason = "ReplicationControllerUpdated"
|
||||
// FailedRcCreateReason is added in a deployment config when it cannot create a new replication
|
||||
// controller.
|
||||
FailedRcCreateReason = "ReplicationControllerCreateError"
|
||||
// NewReplicationControllerReason is added in a deployment config when it creates a new replication
|
||||
// controller.
|
||||
NewReplicationControllerReason = "NewReplicationControllerCreated"
|
||||
// NewRcAvailableReason is added in a deployment config when its newest replication controller is made
|
||||
// available ie. the number of new pods that have passed readiness checks and run for at least
|
||||
// minReadySeconds is at least the minimum available pods that need to run for the deployment config.
|
||||
NewRcAvailableReason = "NewReplicationControllerAvailable"
|
||||
// TimedOutReason is added in a deployment config when its newest replication controller fails to show
|
||||
// any progress within the given deadline (progressDeadlineSeconds).
|
||||
TimedOutReason = "ProgressDeadlineExceeded"
|
||||
// PausedDeployReason is added in a deployment config when it is paused. Lack of progress shouldn't be
|
||||
// estimated once a deployment config is paused.
|
||||
PausedDeployReason = "DeploymentConfigPaused"
|
||||
// ResumedDeployReason is added in a deployment config when it is resumed. Useful for not failing accidentally
|
||||
// deployment configs that paused amidst a rollout.
|
||||
ResumedDeployReason = "DeploymentConfigResumed"
|
||||
)
|
||||
|
||||
// NewDeploymentCondition creates a new deployment condition.
|
||||
func NewDeploymentCondition(condType deployapi.DeploymentConditionType, status api.ConditionStatus, reason, message string) *deployapi.DeploymentCondition {
|
||||
return &deployapi.DeploymentCondition{
|
||||
Type: condType,
|
||||
Status: status,
|
||||
LastTransitionTime: unversioned.Now(),
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// GetDeploymentCondition returns the condition with the provided type.
|
||||
func GetDeploymentCondition(status deployapi.DeploymentConfigStatus, condType deployapi.DeploymentConditionType) *deployapi.DeploymentCondition {
|
||||
for i := range status.Conditions {
|
||||
c := status.Conditions[i]
|
||||
if c.Type == condType {
|
||||
return &c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDeploymentCondition updates the deployment to include the provided condition. If the condition that
|
||||
// we are about to add already exists and has the same status and reason then we are not going to update.
|
||||
func SetDeploymentCondition(status *deployapi.DeploymentConfigStatus, condition deployapi.DeploymentCondition) {
|
||||
currentCond := GetDeploymentCondition(*status, condition.Type)
|
||||
if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason {
|
||||
return
|
||||
}
|
||||
// Preserve lastTransitionTime if we are not switching between statuses of a condition.
|
||||
if currentCond != nil && currentCond.Status == condition.Status {
|
||||
condition.LastTransitionTime = currentCond.LastTransitionTime
|
||||
}
|
||||
newConditions := filterOutCondition(status.Conditions, condition.Type)
|
||||
status.Conditions = append(newConditions, condition)
|
||||
}
|
||||
|
||||
// RemoveDeploymentCondition removes the deployment condition with the provided type.
|
||||
func RemoveDeploymentCondition(status *deployapi.DeploymentConfigStatus, condType deployapi.DeploymentConditionType) {
|
||||
status.Conditions = filterOutCondition(status.Conditions, condType)
|
||||
}
|
||||
|
||||
// filterOutCondition returns a new slice of deployment conditions without conditions with the provided type.
|
||||
func filterOutCondition(conditions []deployapi.DeploymentCondition, condType deployapi.DeploymentConditionType) []deployapi.DeploymentCondition {
|
||||
var newConditions []deployapi.DeploymentCondition
|
||||
for _, c := range conditions {
|
||||
if c.Type == condType {
|
||||
continue
|
||||
}
|
||||
newConditions = append(newConditions, c)
|
||||
}
|
||||
return newConditions
|
||||
}
|
||||
|
||||
// LatestDeploymentNameForConfig returns a stable identifier for config based on its version.
|
||||
func LatestDeploymentNameForConfig(config *deployapi.DeploymentConfig) string {
|
||||
return fmt.Sprintf("%s-%d", config.Name, config.Status.LatestVersion)
|
||||
}
|
||||
|
||||
// LatestDeploymentInfo returns info about the latest deployment for a config,
|
||||
// or nil if there is no latest deployment. The latest deployment is not
|
||||
// always the same as the active deployment.
|
||||
func LatestDeploymentInfo(config *deployapi.DeploymentConfig, deployments []api.ReplicationController) (bool, *api.ReplicationController) {
|
||||
if config.Status.LatestVersion == 0 || len(deployments) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
sort.Sort(ByLatestVersionDesc(deployments))
|
||||
candidate := &deployments[0]
|
||||
return DeploymentVersionFor(candidate) == config.Status.LatestVersion, candidate
|
||||
}
|
||||
|
||||
// ActiveDeployment returns the latest complete deployment, or nil if there is
|
||||
// no such deployment. The active deployment is not always the same as the
|
||||
// latest deployment.
|
||||
func ActiveDeployment(input []api.ReplicationController) *api.ReplicationController {
|
||||
var activeDeployment *api.ReplicationController
|
||||
var lastCompleteDeploymentVersion int64 = 0
|
||||
for i := range input {
|
||||
deployment := &input[i]
|
||||
deploymentVersion := DeploymentVersionFor(deployment)
|
||||
if IsCompleteDeployment(deployment) && deploymentVersion > lastCompleteDeploymentVersion {
|
||||
activeDeployment = deployment
|
||||
lastCompleteDeploymentVersion = deploymentVersion
|
||||
}
|
||||
}
|
||||
return activeDeployment
|
||||
}
|
||||
|
||||
// DeployerPodSuffix is the suffix added to pods created from a deployment
|
||||
const DeployerPodSuffix = "deploy"
|
||||
|
||||
// DeployerPodNameForDeployment returns the name of a pod for a given deployment
|
||||
func DeployerPodNameForDeployment(deployment string) string {
|
||||
return namer.GetPodName(deployment, DeployerPodSuffix)
|
||||
}
|
||||
|
||||
// LabelForDeployment builds a string identifier for a Deployment.
|
||||
func LabelForDeployment(deployment *api.ReplicationController) string {
|
||||
return fmt.Sprintf("%s/%s", deployment.Namespace, deployment.Name)
|
||||
}
|
||||
|
||||
// LabelForDeploymentConfig builds a string identifier for a DeploymentConfig.
|
||||
func LabelForDeploymentConfig(config *deployapi.DeploymentConfig) string {
|
||||
return fmt.Sprintf("%s/%s", config.Namespace, config.Name)
|
||||
}
|
||||
|
||||
// DeploymentNameForConfigVersion returns the name of the version-th deployment
|
||||
// for the config that has the provided name
|
||||
func DeploymentNameForConfigVersion(name string, version int64) string {
|
||||
return fmt.Sprintf("%s-%d", name, version)
|
||||
}
|
||||
|
||||
// ConfigSelector returns a label Selector which can be used to find all
|
||||
// deployments for a DeploymentConfig.
|
||||
//
|
||||
// TODO: Using the annotation constant for now since the value is correct
|
||||
// but we could consider adding a new constant to the public types.
|
||||
func ConfigSelector(name string) labels.Selector {
|
||||
return labels.Set{deployapi.DeploymentConfigAnnotation: name}.AsSelector()
|
||||
}
|
||||
|
||||
// DeployerPodSelector returns a label Selector which can be used to find all
|
||||
// deployer pods associated with a deployment with name.
|
||||
func DeployerPodSelector(name string) labels.Selector {
|
||||
return labels.Set{deployapi.DeployerPodForDeploymentLabel: name}.AsSelector()
|
||||
}
|
||||
|
||||
// AnyDeployerPodSelector returns a label Selector which can be used to find
|
||||
// all deployer pods across all deployments, including hook and custom
|
||||
// deployer pods.
|
||||
func AnyDeployerPodSelector() labels.Selector {
|
||||
sel, _ := labels.Parse(deployapi.DeployerPodForDeploymentLabel)
|
||||
return sel
|
||||
}
|
||||
|
||||
// HasChangeTrigger returns whether the provided deployment configuration has
|
||||
// a config change trigger or not
|
||||
func HasChangeTrigger(config *deployapi.DeploymentConfig) bool {
|
||||
for _, trigger := range config.Spec.Triggers {
|
||||
if trigger.Type == deployapi.DeploymentTriggerOnConfigChange {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasImageChangeTrigger returns whether the provided deployment configuration has
|
||||
// an image change trigger or not.
|
||||
func HasImageChangeTrigger(config *deployapi.DeploymentConfig) bool {
|
||||
for _, trigger := range config.Spec.Triggers {
|
||||
if trigger.Type == deployapi.DeploymentTriggerOnImageChange {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func DeploymentConfigDeepCopy(dc *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) {
|
||||
objCopy, err := api.Scheme.DeepCopy(dc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copied, ok := objCopy.(*deployapi.DeploymentConfig)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected DeploymentConfig, got %#v", objCopy)
|
||||
}
|
||||
return copied, nil
|
||||
}
|
||||
|
||||
func DeploymentDeepCopy(rc *api.ReplicationController) (*api.ReplicationController, error) {
|
||||
objCopy, err := api.Scheme.DeepCopy(rc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copied, ok := objCopy.(*api.ReplicationController)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected ReplicationController, got %#v", objCopy)
|
||||
}
|
||||
return copied, nil
|
||||
}
|
||||
|
||||
// DecodeDeploymentConfig decodes a DeploymentConfig from controller using codec. An error is returned
|
||||
// if the controller doesn't contain an encoded config.
|
||||
func DecodeDeploymentConfig(controller *api.ReplicationController, decoder runtime.Decoder) (*deployapi.DeploymentConfig, error) {
|
||||
encodedConfig := []byte(EncodedDeploymentConfigFor(controller))
|
||||
decoded, err := runtime.Decode(decoder, encodedConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode DeploymentConfig from controller: %v", err)
|
||||
}
|
||||
config, ok := decoded.(*deployapi.DeploymentConfig)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("decoded object from controller is not a DeploymentConfig")
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// EncodeDeploymentConfig encodes config as a string using codec.
|
||||
func EncodeDeploymentConfig(config *deployapi.DeploymentConfig, codec runtime.Codec) (string, error) {
|
||||
bytes, err := runtime.Encode(codec, config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bytes[:]), nil
|
||||
}
|
||||
|
||||
// MakeDeployment creates a deployment represented as a ReplicationController and based on the given
|
||||
// DeploymentConfig. The controller replica count will be zero.
|
||||
func MakeDeployment(config *deployapi.DeploymentConfig, codec runtime.Codec) (*api.ReplicationController, error) {
|
||||
var err error
|
||||
var encodedConfig string
|
||||
|
||||
if encodedConfig, err = EncodeDeploymentConfig(config, codec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deploymentName := LatestDeploymentNameForConfig(config)
|
||||
|
||||
podSpec := api.PodSpec{}
|
||||
if err := api.Scheme.Convert(&config.Spec.Template.Spec, &podSpec, nil); err != nil {
|
||||
return nil, fmt.Errorf("couldn't clone podSpec: %v", err)
|
||||
}
|
||||
|
||||
controllerLabels := make(labels.Set)
|
||||
for k, v := range config.Labels {
|
||||
controllerLabels[k] = v
|
||||
}
|
||||
// Correlate the deployment with the config.
|
||||
// TODO: Using the annotation constant for now since the value is correct
|
||||
// but we could consider adding a new constant to the public types.
|
||||
controllerLabels[deployapi.DeploymentConfigAnnotation] = config.Name
|
||||
|
||||
// Ensure that pods created by this deployment controller can be safely associated back
|
||||
// to the controller, and that multiple deployment controllers for the same config don't
|
||||
// manipulate each others' pods.
|
||||
selector := map[string]string{}
|
||||
for k, v := range config.Spec.Selector {
|
||||
selector[k] = v
|
||||
}
|
||||
selector[deployapi.DeploymentConfigLabel] = config.Name
|
||||
selector[deployapi.DeploymentLabel] = deploymentName
|
||||
|
||||
podLabels := make(labels.Set)
|
||||
for k, v := range config.Spec.Template.Labels {
|
||||
podLabels[k] = v
|
||||
}
|
||||
podLabels[deployapi.DeploymentConfigLabel] = config.Name
|
||||
podLabels[deployapi.DeploymentLabel] = deploymentName
|
||||
|
||||
podAnnotations := make(labels.Set)
|
||||
for k, v := range config.Spec.Template.Annotations {
|
||||
podAnnotations[k] = v
|
||||
}
|
||||
podAnnotations[deployapi.DeploymentAnnotation] = deploymentName
|
||||
podAnnotations[deployapi.DeploymentConfigAnnotation] = config.Name
|
||||
podAnnotations[deployapi.DeploymentVersionAnnotation] = strconv.FormatInt(config.Status.LatestVersion, 10)
|
||||
|
||||
deployment := &api.ReplicationController{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: deploymentName,
|
||||
Namespace: config.Namespace,
|
||||
Annotations: map[string]string{
|
||||
deployapi.DeploymentConfigAnnotation: config.Name,
|
||||
deployapi.DeploymentStatusAnnotation: string(deployapi.DeploymentStatusNew),
|
||||
deployapi.DeploymentEncodedConfigAnnotation: encodedConfig,
|
||||
deployapi.DeploymentVersionAnnotation: strconv.FormatInt(config.Status.LatestVersion, 10),
|
||||
// This is the target replica count for the new deployment.
|
||||
deployapi.DesiredReplicasAnnotation: strconv.Itoa(int(config.Spec.Replicas)),
|
||||
deployapi.DeploymentReplicasAnnotation: strconv.Itoa(0),
|
||||
},
|
||||
Labels: controllerLabels,
|
||||
},
|
||||
Spec: api.ReplicationControllerSpec{
|
||||
// The deployment should be inactive initially
|
||||
Replicas: 0,
|
||||
Selector: selector,
|
||||
Template: &api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: podLabels,
|
||||
Annotations: podAnnotations,
|
||||
},
|
||||
Spec: podSpec,
|
||||
},
|
||||
},
|
||||
}
|
||||
if config.Status.Details != nil && len(config.Status.Details.Message) > 0 {
|
||||
deployment.Annotations[deployapi.DeploymentStatusReasonAnnotation] = config.Status.Details.Message
|
||||
}
|
||||
if value, ok := config.Annotations[deployapi.DeploymentIgnorePodAnnotation]; ok {
|
||||
deployment.Annotations[deployapi.DeploymentIgnorePodAnnotation] = value
|
||||
}
|
||||
|
||||
return deployment, nil
|
||||
}
|
||||
|
||||
// GetReplicaCountForDeployments returns the sum of all replicas for the
|
||||
// given deployments.
|
||||
func GetReplicaCountForDeployments(deployments []api.ReplicationController) int32 {
|
||||
totalReplicaCount := int32(0)
|
||||
for _, deployment := range deployments {
|
||||
totalReplicaCount += deployment.Spec.Replicas
|
||||
}
|
||||
return totalReplicaCount
|
||||
}
|
||||
|
||||
// GetStatusReplicaCountForDeployments returns the sum of the replicas reported in the
|
||||
// status of the given deployments.
|
||||
func GetStatusReplicaCountForDeployments(deployments []api.ReplicationController) int32 {
|
||||
totalReplicaCount := int32(0)
|
||||
for _, deployment := range deployments {
|
||||
totalReplicaCount += deployment.Status.Replicas
|
||||
}
|
||||
return totalReplicaCount
|
||||
}
|
||||
|
||||
// GetAvailablePods returns all the available pods from the provided pod list.
|
||||
func GetAvailablePods(pods []*api.Pod, minReadySeconds int32) int32 {
|
||||
available := int32(0)
|
||||
for i := range pods {
|
||||
pod := pods[i]
|
||||
if kdeplutil.IsPodAvailable(pod, minReadySeconds, time.Now()) {
|
||||
available++
|
||||
}
|
||||
}
|
||||
return available
|
||||
}
|
||||
|
||||
func DeploymentConfigNameFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentConfigAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentNameFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentAnnotation)
|
||||
}
|
||||
|
||||
func DeployerPodNameFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentPodAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentStatusFor(obj runtime.Object) deployapi.DeploymentStatus {
|
||||
return deployapi.DeploymentStatus(annotationFor(obj, deployapi.DeploymentStatusAnnotation))
|
||||
}
|
||||
|
||||
func DeploymentStatusReasonFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentStatusReasonAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentDesiredReplicas(obj runtime.Object) (int32, bool) {
|
||||
return int32AnnotationFor(obj, deployapi.DesiredReplicasAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentReplicas(obj runtime.Object) (int32, bool) {
|
||||
return int32AnnotationFor(obj, deployapi.DeploymentReplicasAnnotation)
|
||||
}
|
||||
|
||||
func EncodedDeploymentConfigFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentEncodedConfigAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentVersionFor(obj runtime.Object) int64 {
|
||||
v, err := strconv.ParseInt(annotationFor(obj, deployapi.DeploymentVersionAnnotation), 10, 64)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func IsDeploymentCancelled(deployment *api.ReplicationController) bool {
|
||||
value := annotationFor(deployment, deployapi.DeploymentCancelledAnnotation)
|
||||
return strings.EqualFold(value, deployapi.DeploymentCancelledAnnotationValue)
|
||||
}
|
||||
|
||||
// HasSynced checks if the provided deployment config has been noticed by the deployment
|
||||
// config controller.
|
||||
func HasSynced(dc *deployapi.DeploymentConfig, generation int64) bool {
|
||||
return dc.Status.ObservedGeneration >= generation
|
||||
}
|
||||
|
||||
// IsOwnedByConfig checks whether the provided replication controller is part of a
|
||||
// deployment configuration.
|
||||
// TODO: Switch to use owner references once we got those working.
|
||||
func IsOwnedByConfig(deployment *api.ReplicationController) bool {
|
||||
_, ok := deployment.Annotations[deployapi.DeploymentConfigAnnotation]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsTerminatedDeployment returns true if the passed deployment has terminated (either
|
||||
// complete or failed).
|
||||
func IsTerminatedDeployment(deployment *api.ReplicationController) bool {
|
||||
return IsCompleteDeployment(deployment) || IsFailedDeployment(deployment)
|
||||
}
|
||||
|
||||
// IsCompleteDeployment returns true if the passed deployment failed.
|
||||
func IsCompleteDeployment(deployment *api.ReplicationController) bool {
|
||||
current := DeploymentStatusFor(deployment)
|
||||
return current == deployapi.DeploymentStatusComplete
|
||||
}
|
||||
|
||||
// IsFailedDeployment returns true if the passed deployment failed.
|
||||
func IsFailedDeployment(deployment *api.ReplicationController) bool {
|
||||
current := DeploymentStatusFor(deployment)
|
||||
return current == deployapi.DeploymentStatusFailed
|
||||
}
|
||||
|
||||
// CanTransitionPhase returns whether it is allowed to go from the current to the next phase.
|
||||
func CanTransitionPhase(current, next deployapi.DeploymentStatus) bool {
|
||||
switch current {
|
||||
case deployapi.DeploymentStatusNew:
|
||||
switch next {
|
||||
case deployapi.DeploymentStatusPending,
|
||||
deployapi.DeploymentStatusRunning,
|
||||
deployapi.DeploymentStatusFailed,
|
||||
deployapi.DeploymentStatusComplete:
|
||||
return true
|
||||
}
|
||||
case deployapi.DeploymentStatusPending:
|
||||
switch next {
|
||||
case deployapi.DeploymentStatusRunning,
|
||||
deployapi.DeploymentStatusFailed,
|
||||
deployapi.DeploymentStatusComplete:
|
||||
return true
|
||||
}
|
||||
case deployapi.DeploymentStatusRunning:
|
||||
switch next {
|
||||
case deployapi.DeploymentStatusFailed, deployapi.DeploymentStatusComplete:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRollingConfig returns true if the strategy type is a rolling update.
|
||||
func IsRollingConfig(config *deployapi.DeploymentConfig) bool {
|
||||
return config.Spec.Strategy.Type == deployapi.DeploymentStrategyTypeRolling
|
||||
}
|
||||
|
||||
// IsProgressing expects a state deployment config and its updated status in order to
|
||||
// determine if there is any progress.
|
||||
func IsProgressing(config deployapi.DeploymentConfig, newStatus deployapi.DeploymentConfigStatus) bool {
|
||||
oldStatusOldReplicas := config.Status.Replicas - config.Status.UpdatedReplicas
|
||||
newStatusOldReplicas := newStatus.Replicas - newStatus.UpdatedReplicas
|
||||
|
||||
return (newStatus.UpdatedReplicas > config.Status.UpdatedReplicas) || (newStatusOldReplicas < oldStatusOldReplicas)
|
||||
}
|
||||
|
||||
// MaxUnavailable returns the maximum unavailable pods a rolling deployment config can take.
|
||||
func MaxUnavailable(config deployapi.DeploymentConfig) int32 {
|
||||
if !IsRollingConfig(&config) {
|
||||
return int32(0)
|
||||
}
|
||||
// Error caught by validation
|
||||
_, maxUnavailable, _ := kdeplutil.ResolveFenceposts(&config.Spec.Strategy.RollingParams.MaxSurge, &config.Spec.Strategy.RollingParams.MaxUnavailable, config.Spec.Replicas)
|
||||
return maxUnavailable
|
||||
}
|
||||
|
||||
// MaxSurge returns the maximum surge pods a rolling deployment config can take.
|
||||
func MaxSurge(config deployapi.DeploymentConfig) int32 {
|
||||
if !IsRollingConfig(&config) {
|
||||
return int32(0)
|
||||
}
|
||||
// Error caught by validation
|
||||
maxSurge, _, _ := kdeplutil.ResolveFenceposts(&config.Spec.Strategy.RollingParams.MaxSurge, &config.Spec.Strategy.RollingParams.MaxUnavailable, config.Spec.Replicas)
|
||||
return maxSurge
|
||||
}
|
||||
|
||||
// annotationFor returns the annotation with key for obj.
|
||||
func annotationFor(obj runtime.Object, key string) string {
|
||||
meta, err := api.ObjectMetaFor(obj)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return meta.Annotations[key]
|
||||
}
|
||||
|
||||
func int32AnnotationFor(obj runtime.Object, key string) (int32, bool) {
|
||||
s := annotationFor(obj, key)
|
||||
if len(s) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
i, err := strconv.ParseInt(s, 10, 32)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return int32(i), true
|
||||
}
|
||||
|
||||
// DeploymentsForCleanup determines which deployments for a configuration are relevant for the
|
||||
// revision history limit quota
|
||||
func DeploymentsForCleanup(configuration *deployapi.DeploymentConfig, deployments []api.ReplicationController) []api.ReplicationController {
|
||||
// if the past deployment quota has been exceeded, we need to prune the oldest deployments
|
||||
// until we are not exceeding the quota any longer, so we sort oldest first
|
||||
sort.Sort(ByLatestVersionAsc(deployments))
|
||||
|
||||
relevantDeployments := []api.ReplicationController{}
|
||||
activeDeployment := ActiveDeployment(deployments)
|
||||
if activeDeployment == nil {
|
||||
// if cleanup policy is set but no successful deployments have happened, there will be
|
||||
// no active deployment. We can consider all of the deployments in this case except for
|
||||
// the latest one
|
||||
for i := range deployments {
|
||||
deployment := &deployments[i]
|
||||
if DeploymentVersionFor(deployment) != configuration.Status.LatestVersion {
|
||||
relevantDeployments = append(relevantDeployments, *deployment)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if there is an active deployment, we need to filter out any deployments that we don't
|
||||
// care about, namely the active deployment and any newer deployments
|
||||
for i := range deployments {
|
||||
deployment := &deployments[i]
|
||||
if deployment != activeDeployment && DeploymentVersionFor(deployment) < DeploymentVersionFor(activeDeployment) {
|
||||
relevantDeployments = append(relevantDeployments, *deployment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return relevantDeployments
|
||||
}
|
||||
|
||||
// WaitForRunningDeployerPod waits a given period of time until the deployer pod
|
||||
// for given replication controller is not running.
|
||||
func WaitForRunningDeployerPod(podClient kclient.PodsNamespacer, rc *api.ReplicationController, timeout time.Duration) error {
|
||||
podName := DeployerPodNameForDeployment(rc.Name)
|
||||
canGetLogs := func(p *api.Pod) bool {
|
||||
return api.PodSucceeded == p.Status.Phase || api.PodFailed == p.Status.Phase || api.PodRunning == p.Status.Phase
|
||||
}
|
||||
pod, err := podClient.Pods(rc.Namespace).Get(podName)
|
||||
if err == nil && canGetLogs(pod) {
|
||||
return nil
|
||||
}
|
||||
watcher, err := podClient.Pods(rc.Namespace).Watch(
|
||||
api.ListOptions{
|
||||
FieldSelector: fields.Set{"metadata.name": podName}.AsSelector(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer watcher.Stop()
|
||||
if _, err := watch.Until(timeout, watcher, func(e watch.Event) (bool, error) {
|
||||
if e.Type == watch.Error {
|
||||
return false, fmt.Errorf("encountered error while watching for pod: %v", e.Object)
|
||||
}
|
||||
obj, isPod := e.Object.(*api.Pod)
|
||||
if !isPod {
|
||||
return false, errors.New("received unknown object while watching for pods")
|
||||
}
|
||||
return canGetLogs(obj), nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ByLatestVersionAsc sorts deployments by LatestVersion ascending.
|
||||
type ByLatestVersionAsc []api.ReplicationController
|
||||
|
||||
func (d ByLatestVersionAsc) Len() int { return len(d) }
|
||||
func (d ByLatestVersionAsc) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||
func (d ByLatestVersionAsc) Less(i, j int) bool {
|
||||
return DeploymentVersionFor(&d[i]) < DeploymentVersionFor(&d[j])
|
||||
}
|
||||
|
||||
// ByLatestVersionDesc sorts deployments by LatestVersion descending.
|
||||
type ByLatestVersionDesc []api.ReplicationController
|
||||
|
||||
func (d ByLatestVersionDesc) Len() int { return len(d) }
|
||||
func (d ByLatestVersionDesc) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||
func (d ByLatestVersionDesc) Less(i, j int) bool {
|
||||
return DeploymentVersionFor(&d[j]) < DeploymentVersionFor(&d[i])
|
||||
}
|
||||
|
||||
// ByMostRecent sorts deployments by most recently created.
|
||||
type ByMostRecent []*api.ReplicationController
|
||||
|
||||
func (s ByMostRecent) Len() int { return len(s) }
|
||||
func (s ByMostRecent) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s ByMostRecent) Less(i, j int) bool {
|
||||
return !s[i].CreationTimestamp.Before(s[j].CreationTimestamp)
|
||||
}
|
||||
+1
-1
@@ -77,7 +77,7 @@ var (
|
||||
"ipv6": IPV6FormatChecker{},
|
||||
"uri": URIFormatChecker{},
|
||||
"uuid": UUIDFormatChecker{},
|
||||
"regex": UUIDFormatChecker{},
|
||||
"regex": RegexFormatChecker{},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user