From e825f3aafdb522bbdac626387f3c9e7d489e35a7 Mon Sep 17 00:00:00 2001 From: Alvaro Aleman Date: Sat, 10 Aug 2019 13:30:42 +0200 Subject: [PATCH 1/6] :sparkles: Allow fine-grained configuration of log/zap --- pkg/log/zap/zap.go | 130 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 113 insertions(+), 17 deletions(-) diff --git a/pkg/log/zap/zap.go b/pkg/log/zap/zap.go index 5812e85bfb..732ceea364 100644 --- a/pkg/log/zap/zap.go +++ b/pkg/log/zap/zap.go @@ -29,10 +29,23 @@ import ( "go.uber.org/zap/zapcore" ) +// New returns a brand new Logger configured with Opts. It +// uses KubeAwareEncoder which adds Type information and +// Namespace/Name to the log. +func New(opts ...Opts) logr.Logger { + return zapr.NewLogger(NewRaw(opts...)) +} + // Logger is a Logger implementation. // If development is true, a Zap development config will be used // (stacktraces on warnings, no sampling), otherwise a Zap production // config will be used (stacktraces on errors, sampling). +// +// Deprecated, use New() and the functional opts pattern instead: +// +// New(func(o *Options){ +// o.Development: development, +// }) func Logger(development bool) logr.Logger { return LoggerTo(os.Stderr, development) } @@ -40,34 +53,117 @@ func Logger(development bool) logr.Logger { // LoggerTo returns a new Logger implementation using Zap which logs // to the given destination, instead of stderr. It otherwise behaves like // ZapLogger. +// +// Deprecated, use New() and the functional opts pattern instead: +// +// New(func(o *Options){ +// o.Development: development, +// o.DestWriter: writer, +// }) func LoggerTo(destWriter io.Writer, development bool) logr.Logger { return zapr.NewLogger(RawLoggerTo(destWriter, development)) } // RawLoggerTo returns a new zap.Logger configured with KubeAwareEncoder // which logs to a given destination +// +// Deprecated, use NewRaw() and the functional opts pattern instead: +// +// NewRaw(func(o *Options){ +// o.Development: development, +// }) func RawLoggerTo(destWriter io.Writer, development bool, opts ...zap.Option) *zap.Logger { - // this basically mimics NewConfig, but with a custom sink - sink := zapcore.AddSync(destWriter) - - var enc zapcore.Encoder - var lvl zap.AtomicLevel - if development { - encCfg := zap.NewDevelopmentEncoderConfig() - enc = zapcore.NewConsoleEncoder(encCfg) - lvl = zap.NewAtomicLevelAt(zap.DebugLevel) - opts = append(opts, zap.Development(), zap.AddStacktrace(zap.ErrorLevel)) + o := func(o *Options) { + o.DestWritter = destWriter + o.Development = development + o.ZapOpts = opts + } + return NewRaw(o) +} + +// Opts allows to manipulate Options +type Opts func(*Options) + +// Options contains all possible settings +type Options struct { + // If Development is true, a Zap development config will be used + // (stacktraces on warnings, no sampling), otherwise a Zap production + // config will be used (stacktraces on errors, sampling). + Development bool + // The encoder to use, defaults to console when Development is true + // and JSON otherwise + Encoder zapcore.Encoder + // The destination to write to, defaults to os.Stderr + DestWritter io.Writer + // The level to use, defaults to Debug when Development is true and + // Info otherwise + Level *zap.AtomicLevel + // StacktraceLevel is the level at and above which stacktraces will + // be recorded for all messages. Defaults to Warn when Development + // is true and Error otherwise + StacktraceLevel *zap.AtomicLevel + // Raw zap.Options to configure on the underlying zap logger + ZapOpts []zap.Option +} + +// addDefaults adds defaults to the Options +func (o *Options) addDefaults() { + if o.DestWritter == nil { + o.DestWritter = os.Stderr + } + + if o.Development { + if o.Encoder == nil { + encCfg := zap.NewDevelopmentEncoderConfig() + o.Encoder = zapcore.NewConsoleEncoder(encCfg) + } + if o.Level == nil { + lvl := zap.NewAtomicLevelAt(zap.DebugLevel) + o.Level = &lvl + } + if o.StacktraceLevel == nil { + lvl := zap.NewAtomicLevelAt(zap.WarnLevel) + o.StacktraceLevel = &lvl + } + o.ZapOpts = append(o.ZapOpts, zap.Development()) + } else { - encCfg := zap.NewProductionEncoderConfig() - enc = zapcore.NewJSONEncoder(encCfg) - lvl = zap.NewAtomicLevelAt(zap.InfoLevel) - opts = append(opts, zap.AddStacktrace(zap.WarnLevel), + if o.Encoder == nil { + encCfg := zap.NewProductionEncoderConfig() + o.Encoder = zapcore.NewJSONEncoder(encCfg) + } + if o.Level == nil { + lvl := zap.NewAtomicLevelAt(zap.InfoLevel) + o.Level = &lvl + } + if o.StacktraceLevel == nil { + lvl := zap.NewAtomicLevelAt(zap.ErrorLevel) + o.StacktraceLevel = &lvl + } + o.ZapOpts = append(o.ZapOpts, zap.WrapCore(func(core zapcore.Core) zapcore.Core { return zapcore.NewSampler(core, time.Second, 100, 100) })) } - opts = append(opts, zap.AddCallerSkip(1), zap.ErrorOutput(sink)) - log := zap.New(zapcore.NewCore(&KubeAwareEncoder{Encoder: enc, Verbose: development}, sink, lvl)) - log = log.WithOptions(opts...) + + o.ZapOpts = append(o.ZapOpts, zap.AddStacktrace(o.StacktraceLevel)) +} + +// NewRaw returns a new zap.Logger configured with the passed Opts +// or their defaults. It uses KubeAwareEncoder which adds Type +// information and Namespace/Name to the log. +func NewRaw(opts ...Opts) *zap.Logger { + o := &Options{} + for _, opt := range opts { + opt(o) + } + o.addDefaults() + + // this basically mimics NewConfig, but with a custom sink + sink := zapcore.AddSync(o.DestWritter) + + o.ZapOpts = append(o.ZapOpts, zap.AddCallerSkip(1), zap.ErrorOutput(sink)) + log := zap.New(zapcore.NewCore(&KubeAwareEncoder{Encoder: o.Encoder, Verbose: o.Development}, sink, *o.Level)) + log = log.WithOptions(o.ZapOpts...) return log } From 3332a0a279805d249018127ce3d8b6a927f0bdb3 Mon Sep 17 00:00:00 2001 From: ialidzhikov Date: Sat, 31 Aug 2019 23:14:34 +0300 Subject: [PATCH 2/6] Update example dir in godoc Signed-off-by: ialidzhikov --- pkg/doc.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/doc.go b/pkg/doc.go index d573953945..de991dd399 100644 --- a/pkg/doc.go +++ b/pkg/doc.go @@ -148,7 +148,7 @@ Usage The following example shows creating a new Controller program which Reconciles ReplicaSet objects in response to Pod or ReplicaSet events. The Reconciler function simply adds a label to the ReplicaSet. -See the example/main.go for a usage example. +See the examples/builtins/main.go for a usage example. Controller Example @@ -159,7 +159,7 @@ Controller Example 1.2 Pod (created by ReplicaSet) -> handler.EnqueueRequestForOwnerHandler - enqueue a Request with the Owning ReplicaSet Namespace and Name. -2. reconcile ReplicaSet in response to an event +2. Reconcile ReplicaSet in response to an event 2.1 ReplicaSet object created -> Read ReplicaSet, try to read Pods -> if is missing create Pods. From a093650d566b77bff3e31aaf3122a0b36175d56e Mon Sep 17 00:00:00 2001 From: Gabe Rosenhouse Date: Sun, 1 Sep 2019 22:37:03 -0700 Subject: [PATCH 3/6] docs: fix spelling of function EnqueueRequestsFromMapFunc Other Enqueue... functions are singular, but this one is plural --- pkg/doc.go | 2 +- pkg/handler/eventhandler.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/doc.go b/pkg/doc.go index d573953945..aaa693cb4b 100644 --- a/pkg/doc.go +++ b/pkg/doc.go @@ -171,7 +171,7 @@ Watching and EventHandling Controllers may Watch multiple Kinds of objects (e.g. Pods, ReplicaSets and Deployments), but they reconcile only a single Type. When one Type of object must be updated in response to changes in another Type of object, -an EnqueueRequestFromMapFunc may be used to map events from one type to another. e.g. Respond to a cluster resize +an EnqueueRequestsFromMapFunc may be used to map events from one type to another. e.g. Respond to a cluster resize event (add / delete Node) by re-reconciling all instances of some API. A Deployment Controller might use an EnqueueRequestForObject and EnqueueRequestForOwner to: diff --git a/pkg/handler/eventhandler.go b/pkg/handler/eventhandler.go index 840cf5b892..c9b93f8b97 100644 --- a/pkg/handler/eventhandler.go +++ b/pkg/handler/eventhandler.go @@ -33,7 +33,7 @@ import ( // * Use EnqueueRequestForOwner to reconcile the owner of the object the event is for // - do this for events for the types the Controller creates. (e.g. ReplicaSets created by a Deployment Controller) // -// * Use EnqueueRequestFromMapFunc to transform an event for an object to a reconcile of an object +// * Use EnqueueRequestsFromMapFunc to transform an event for an object to a reconcile of an object // of a different type - do this for events for types the Controller may be interested in, but doesn't create. // (e.g. If Foo responds to cluster size events, map Node events to Foo objects.) // From 042139891e1197206deb3e80f5d614a9c8405962 Mon Sep 17 00:00:00 2001 From: Alvaro Aleman Date: Sun, 1 Sep 2019 21:00:45 +0200 Subject: [PATCH 4/6] :running: Add script to run tests in Prow --- hack/ci-check-everything.sh | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100755 hack/ci-check-everything.sh diff --git a/hack/ci-check-everything.sh b/hack/ci-check-everything.sh new file mode 100755 index 0000000000..5fbaa50980 --- /dev/null +++ b/hack/ci-check-everything.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# Copyright 2018 The Kubernetes Authors. +# +# 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. + +set -e + +export GO111MODULE=on +export TRACE=1 + +# Not included or existing by default in Prow +export PATH=$(go env GOPATH)/bin:$PATH +mkdir -p $(go env GOPATH)/bin + +echo "Installing dep" +export DEP_RELEASE_TAG=v0.5.4 +curl --location --silent --retry 5 --fail https://raw.githubusercontent.com/golang/dep/master/install.sh | sh +echo "Finished installing dep" + +echo "Installing golangci-lint" +curl --location --silent --retry 5 --fail https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.15.0 +echo "Finished installing golangci-lint" + +$(dirname ${BASH_SOURCE})/check-everything.sh From 464a08dd26543c076f5aba1afc736fe72bda1863 Mon Sep 17 00:00:00 2001 From: Erick Fejta Date: Tue, 3 Sep 2019 15:53:18 -0700 Subject: [PATCH 5/6] Minor upgrade of gnostic to v0.3.1 --- go.mod | 2 +- go.sum | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index e8de3c4807..7cec2234d6 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/gogo/protobuf v1.1.1 // indirect github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 // indirect github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect - github.com/googleapis/gnostic v0.2.0 // indirect + github.com/googleapis/gnostic v0.3.1 // indirect github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 // indirect github.com/imdario/mergo v0.3.6 // indirect github.com/json-iterator/go v1.1.5 // indirect diff --git a/go.sum b/go.sum index 62a16bcba4..c45ebc20bb 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -19,10 +20,14 @@ github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 h1:u4bArs140e9+A github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= +github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw= github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -102,6 +107,8 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b h1:aBGgKJUM9Hk/3AE8WaZIApnTxG35kbuQba2w+SXqezo= k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8 h1:q1Qvjzs/iEdXF6A1a8H3AKVFDzJNcJn3nXMs6R6qFtA= From 940797eaa6c11acc91a026fe3797eea180e12f31 Mon Sep 17 00:00:00 2001 From: Eric Stroczynski Date: Mon, 26 Aug 2019 14:00:01 -0700 Subject: [PATCH 6/6] pkg/client/options.go: MatchingFieldsSelector and MatchingLabelsSelector are wrapper types for fields.Selector and labels.Selector, respectively, implementing ListOption and DeleteAllOfOption --- pkg/client/options.go | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/pkg/client/options.go b/pkg/client/options.go index 4007a67657..f8251ce25e 100644 --- a/pkg/client/options.go +++ b/pkg/client/options.go @@ -313,6 +313,21 @@ func (m MatchingLabels) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) { m.ApplyToList(&opts.ListOptions) } +// MatchingLabelsSelector filters the list/delete operation on the given label +// selector (or index in the case of cached lists). A struct is used because +// labels.Selector is an interface, which cannot be aliased. +type MatchingLabelsSelector struct { + labels.Selector +} + +func (m MatchingLabelsSelector) ApplyToList(opts *ListOptions) { + opts.LabelSelector = m +} + +func (m MatchingLabelsSelector) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) { + m.ApplyToList(&opts.ListOptions) +} + // MatchingField filters the list operation on the given field selector // (or index in the case of cached lists). // @@ -321,7 +336,7 @@ func MatchingField(name, val string) MatchingFields { return MatchingFields{name: val} } -// MatchingField filters the list/delete operation on the given field selector +// MatchingField filters the list/delete operation on the given field Set // (or index in the case of cached lists). type MatchingFields fields.Set @@ -335,6 +350,21 @@ func (m MatchingFields) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) { m.ApplyToList(&opts.ListOptions) } +// MatchingFieldsSelector filters the list/delete operation on the given field +// selector (or index in the case of cached lists). A struct is used because +// fields.Selector is an interface, which cannot be aliased. +type MatchingFieldsSelector struct { + fields.Selector +} + +func (m MatchingFieldsSelector) ApplyToList(opts *ListOptions) { + opts.FieldSelector = m +} + +func (m MatchingFieldsSelector) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) { + m.ApplyToList(&opts.ListOptions) +} + // InNamespace restricts the list/delete operation to the given namespace. type InNamespace string