From 7e4605cda0ef0ff1cd351268fddbdecc11f473ad Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Tue, 17 Sep 2019 13:49:17 +0200 Subject: [PATCH 01/29] Cleanup controller registration to the runtime 0.2.0 --- .github/CODEOWNERS | 1 - cmd/manager/main.go | 73 +++--- go.mod | 22 +- go.sum | 223 +----------------- pkg/controller/controller.go | 4 - .../operator/operator_controller.go | 54 +---- .../operatorversion_controller.go | 50 +--- pkg/test/harness.go | 5 - pkg/webhook/webhook.go | 36 --- 9 files changed, 81 insertions(+), 387 deletions(-) delete mode 100644 pkg/webhook/webhook.go diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 35b01cc43..e9de59efa 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -16,5 +16,4 @@ /pkg/kudoctl/ @fabianbaier @kensipe @alenkacz /pkg/test/ @jbarrick-mesosphere /pkg/util/ @gerred @runyontr @kensipe -/pkg/webhook/ @gerred @runyontr @kensipe /site/ @guenter @gerred diff --git a/cmd/manager/main.go b/cmd/manager/main.go index a6ea84280..0cea36775 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -17,20 +17,36 @@ package main import ( "fmt" + "github.com/kudobuilder/kudo/pkg/controller/operatorversion" "os" + "github.com/kudobuilder/kudo/pkg/controller/operator" + + "k8s.io/apimachinery/pkg/runtime" + "github.com/kudobuilder/kudo/pkg/version" - "github.com/kudobuilder/kudo/pkg/apis" + kudov1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" "github.com/kudobuilder/kudo/pkg/controller" - "github.com/kudobuilder/kudo/pkg/webhook" + appsv1 "k8s.io/api/apps/v1" + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" - "sigs.k8s.io/controller-runtime/pkg/client/config" - "sigs.k8s.io/controller-runtime/pkg/manager" + ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - "sigs.k8s.io/controller-runtime/pkg/runtime/signals" ) +var ( + scheme = runtime.NewScheme() +) + +func init() { + appsv1.AddToScheme(scheme) + batchv1.AddToScheme(scheme) + corev1.AddToScheme(scheme) + kudov1alpha1.AddToScheme(scheme) +} + func main() { logf.SetLogger(logf.ZapLogger(false)) log := logf.Log.WithName("entrypoint") @@ -38,47 +54,48 @@ func main() { // Get version of KUDO log.Info(fmt.Sprintf("KUDO Version: %s", fmt.Sprintf("%#v", version.Get()))) - // Get a config to talk to the apiserver - log.Info("setting up client for manager") - cfg, err := config.GetConfig() - if err != nil { - log.Error(err, "unable to set up client config") - os.Exit(1) - } - - // Create a new Cmd to provide shared dependencies and start components + // create new controller-runtime manager log.Info("setting up manager") - mgr, err := manager.New(cfg, manager.Options{}) + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + }) if err != nil { - log.Error(err, "unable to set up overall controller manager") + log.Error(err, "unable to start manager") os.Exit(1) } log.Info("Registering Components.") - // Setup Scheme for all resources - log.Info("setting up scheme") - if err := apis.AddToScheme(mgr.GetScheme()); err != nil { - log.Error(err, "unable add APIs to scheme") + // Setup all Controllers + + log.Info("Setting up operator controller") + err = (&operator.Reconciler{ + Client: mgr.GetClient(), + }).SetupWithManager(mgr) + if err != nil { + log.Error(err, "unable to register operator controller to the manager") os.Exit(1) } - // Setup all Controllers - log.Info("Setting up controller") - if err := controller.AddControllersToManager(mgr); err != nil { - log.Error(err, "unable to register controllers to the manager") + log.Info("Setting up operator version controller") + err = (&operatorversion.Reconciler{ + Client: mgr.GetClient(), + }).SetupWithManager(mgr) + if err != nil { + log.Error(err, "unable to register operator controller to the manager") os.Exit(1) } - log.Info("setting up webhooks") - if err := webhook.AddToManager(mgr); err != nil { - log.Error(err, "unable to register webhooks to the manager") + log.Info("Setting up controllers") + // TODO this still registers instance and PE controller, this will be refactored in the next phase + if err := controller.AddControllersToManager(mgr); err != nil { + log.Error(err, "unable to register controllers to the manager") os.Exit(1) } // Start the Cmd log.Info("Starting the Cmd.") - if err := mgr.Start(signals.SetupSignalHandler()); err != nil { + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { log.Error(err, "unable to run the manager") os.Exit(1) } diff --git a/go.mod b/go.mod index 5d055c7f4..3bf0f73eb 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,11 @@ module github.com/kudobuilder/kudo go 1.13 require ( - cloud.google.com/go v0.38.0 + cloud.google.com/go v0.38.0 // indirect github.com/Azure/go-autorest/autorest v0.5.0 // indirect github.com/Masterminds/goutils v1.1.0 // indirect github.com/Masterminds/semver v1.4.2 + github.com/Microsoft/go-winio v0.4.14 // indirect github.com/docker/distribution v2.7.1+incompatible // indirect github.com/docker/docker v1.13.1 github.com/docker/go-connections v0.4.0 // indirect @@ -17,16 +18,13 @@ require ( github.com/go-playground/universal-translator v0.16.0 // indirect github.com/go-test/deep v1.0.1 github.com/gogo/protobuf v1.3.0 // indirect - github.com/golang/mock v1.3.1 // indirect - github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d // indirect - github.com/golangci/golangci-lint v1.18.0 // indirect - github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039 // indirect + github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect github.com/google/go-github v17.0.0+incompatible github.com/google/go-querystring v1.0.0 // indirect github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf github.com/gophercloud/gophercloud v0.2.0 // indirect - github.com/gostaticanalysis/analysisutil v0.0.3 // indirect github.com/gosuri/uitable v0.0.3 + github.com/grpc-ecosystem/grpc-gateway v1.9.0 // indirect github.com/hashicorp/golang-lru v0.5.1 // indirect github.com/huandu/xstrings v1.2.0 // indirect github.com/imdario/mergo v0.3.7 // indirect @@ -36,27 +34,27 @@ require ( github.com/masterminds/sprig v2.18.0+incompatible github.com/mattn/go-isatty v0.0.9 // indirect github.com/mattn/go-runewidth v0.0.4 // indirect - github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d // indirect github.com/onsi/gomega v1.5.0 github.com/opencontainers/go-digest v1.0.0-rc1 // indirect github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 // indirect - github.com/pelletier/go-toml v1.4.0 // indirect github.com/pkg/errors v0.8.1 github.com/pmezard/go-difflib v1.0.0 + github.com/prometheus/client_golang v0.9.3 // indirect github.com/sirupsen/logrus v1.4.2 // indirect github.com/spf13/afero v1.2.2 github.com/spf13/cobra v0.0.5 - github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.3 - github.com/spf13/viper v1.4.0 // indirect github.com/stretchr/testify v1.3.0 - github.com/ultraware/funlen v0.0.2 // indirect github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca + go.uber.org/atomic v1.4.0 // indirect + go.uber.org/zap v1.10.0 // indirect golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a // indirect golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5 // indirect + golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect golang.org/x/tools v0.0.0-20190909030654-5b82db07426d google.golang.org/appengine v1.5.0 // indirect + google.golang.org/grpc v1.21.0 // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/validator.v9 v9.27.0 gopkg.in/yaml.v2 v2.2.2 @@ -66,11 +64,9 @@ require ( k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible k8s.io/code-generator v0.0.0-20181117043124-c2090bec4d9b - mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f // indirect sigs.k8s.io/controller-runtime v0.2.0 sigs.k8s.io/controller-tools v0.2.0 sigs.k8s.io/kind v0.5.1 sigs.k8s.io/kustomize v2.0.3+incompatible sigs.k8s.io/yaml v1.1.0 - sourcegraph.com/sqs/pbtypes v1.0.0 // indirect ) diff --git a/go.sum b/go.sum index 3e32164e2..0263716e1 100644 --- a/go.sum +++ b/go.sum @@ -22,13 +22,11 @@ github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RP github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenPeeDeeP/depguard v1.0.0 h1:k9QF73nrHT3nPLz3lu6G5s+3Hi8Je36ODr1F5gjAXXM= -github.com/OpenPeeDeeP/depguard v1.0.0/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= @@ -36,7 +34,6 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= @@ -51,12 +48,9 @@ github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXG github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -79,15 +73,10 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.0+incompatible h1:YKhDcF/NL19iSAQcyCATL1MkFXCzxfdaTiuJKr18Ank= -github.com/emicklei/go-restful v2.9.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.6+incompatible h1:tfrHha8zJ01ywiOEC1miGY8st1/igzWB8OmvPgoYX7w= github.com/emicklei/go-restful v2.9.6+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 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/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -95,39 +84,23 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-critic/go-critic v0.3.5-0.20190526074819-1df300866540 h1:djv/qAomOVj8voCHt0M0OYwR/4vfDq1zNKSPKjJCexs= -github.com/go-critic/go-critic v0.3.5-0.20190526074819-1df300866540/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0= -github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54= github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.18.0 h1:KVRzjXpMzgdM4GEMDmDTnGcY5yBwGWreJwmmk4k35yU= -github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.18.0 h1:oP2OUNdG1l2r5kYhrfVMXO54gWmzcfAwP/GFuHpNTkE= -github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4= -github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.18.0 h1:1DU8Km1MRGv9Pj7BNLmkA+umwTStwDHttXvx3NhJA70= -github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= @@ -137,33 +110,8 @@ github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEK github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg= github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g= -github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= -github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8= -github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= -github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ= -github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg= -github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k= -github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= -github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= -github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk= -github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg= -github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= -github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks= -github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= -github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4= -github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= -github.com/go-toolsmith/typep v1.0.0 h1:zKymWyA1TRYvqYrYDrfEMZULyrhcnGY3x7LDKU2XQaA= -github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= -github.com/gobuffalo/envy v1.6.15 h1:OsV5vOpHYUpP7ZLS6sem1y40/lNX1BZj+ynMiRi21lQ= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/flect v0.1.5 h1:xpKq9ap8MbYfhuPCF0dBH854Gp9CxZjr/IocxELFflo= github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= -github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= @@ -175,58 +123,16 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 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.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 h1:YYWNAGTKWhKpcLLt7aSj/odlKrSrelQwlovBpDuf19w= -github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= -github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw= -github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= -github.com/golangci/go-tools v0.0.0-20190318055746-e32c54105b7c h1:/7detzz5stiXWPzkTlPTzkBEIIE4WGpppBJYjKqBiPI= -github.com/golangci/go-tools v0.0.0-20190318055746-e32c54105b7c/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM= -github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3 h1:pe9JHs3cHHDQgOFXJJdYkK6fLz2PWyYtP4hthoCMvs8= -github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o= -github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee h1:J2XAy40+7yz70uaOiMbNnluTg7gyQhtGqLQncQh+4J8= -github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= -github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d h1:pXTK/gkVNs7Zyy7WKgLXmpQ5bHTrq5GDsp8R9Qs67g0= -github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= -github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98 h1:0OkFarm1Zy2CjCiDKfK9XHgmc2wbDlRMD2hD8anAJHU= -github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= -github.com/golangci/golangci-lint v1.18.0 h1:XmQgfcLofSG/6AsQuQqmLizB+3GggD+o6ObBG9L+VMM= -github.com/golangci/golangci-lint v1.18.0/go.mod h1:kaqo8l0OZKYPtjNmG4z4HrWLgcYNIJ9B9q3LWri9uLg= -github.com/golangci/gosec v0.0.0-20190211064107-66fb7fc33547 h1:fUdgm/BdKvwOHxg5AhNbkNRp2mSy8sxTXyBVs/laQHo= -github.com/golangci/gosec v0.0.0-20190211064107-66fb7fc33547/go.mod h1:0qUabqiIQgfmlAmulqxyiGkkyF6/tOGSnY2cnPVwrzU= -github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc h1:gLLhTLMk2/SutryVJ6D4VZCU3CUqr8YloG7FPIBWFpI= -github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU= -github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217 h1:En/tZdwhAn0JNwLuXzP3k2RVtMqMmOEK7Yu/g3tmtJE= -github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770 h1:EL/O5HGrF7Jaq0yNhBLucz9hTuRzj2LdwGBOaENgxIk= -github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= -github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us= -github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI= -github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0 h1:HVfrLniijszjS1aiNg8JbBMO2+E1WIQ+j/gL4SQqGPg= -github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= -github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039 h1:XQKc8IYQOeRwVs36tDrEmTgDgP88d5iEURwpmtiAlOM= -github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= @@ -257,15 +163,8 @@ github.com/gophercloud/gophercloud v0.2.0 h1:lD2Bce2xBAMNNcFZ0dObTpXkGLlVIb33RPV github.com/gophercloud/gophercloud v0.2.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw= -github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/gostaticanalysis/analysisutil v0.0.3 h1:iwp+5/UAyzQSFgQ4uR2sni99sJ8Eo9DEacKWM5pekIg= -github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gosuri/uitable v0.0.3 h1:9ZY4qCODg6JL1Ui4dL9LqCF4ghWnAOSV2h7xG98SkHE= github.com/gosuri/uitable v0.0.3/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0 h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -274,8 +173,6 @@ github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCO github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno= -github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -287,7 +184,6 @@ github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -298,13 +194,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= @@ -318,40 +209,28 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= -github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481 h1:IaSjLMT6WvkoZZjspGxy3rdaTEmWLoRm49WbtVUi9sA= github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/masterminds/sprig v2.18.0+incompatible h1:x2CrsC8ZPYlxIC1dxAng4P46a1Fv7hbOJMadfwG7k+k= github.com/masterminds/sprig v2.18.0+incompatible/go.mod h1:MI10VsRNvEV+FKEkd5Ptqq78DEhmrb3fCBgby33c0oY= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= -github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238 h1:+MZW2uvHgN8kYvksEN3f7eFL2wpzk0GxmlFsMybWc7E= -github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -361,14 +240,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= -github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663 h1:Ri1EhipkbhWsffPJ3IPlrb4SkTOPa2PfRXp3jchBczw= -github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= -github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E= -github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -388,11 +261,7 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 h1:zNBQb37RGLmJybyMcs983HfUfpkw9OTFD9tbBfAViHE= github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= -github.com/pelletier/go-toml v1.1.0 h1:cmiOvKzEunMsAxyhXSzpL5Q1CRKpVv0KQsnAIcSEVYM= -github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= -github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= @@ -421,18 +290,9 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -440,16 +300,10 @@ github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sourcegraph/go-diff v0.5.1 h1:gO6i5zugwzo1RVTvgvfwCOSVegNuvnNi6bAD1QCmkHs= -github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg= -github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -457,21 +311,12 @@ github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec h1:2ZXvIUGghLpdTVHR1UfvfrzoVlZaE/yOWC5LueIHZig= -github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.0.2 h1:Ncr3ZIuJn322w2k1qmzXDnkLAdQMlJqBa9kfAH+irso= -github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -479,24 +324,10 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/timakin/bodyclose v0.0.0-20190721030226-87058b9bfcec h1:AmoEvWAO3nDx1MEcMzPh+GzOOIA5Znpv6++c7bePPY0= -github.com/timakin/bodyclose v0.0.0-20190721030226-87058b9bfcec/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ultraware/funlen v0.0.1 h1:UeC9tpM4wNWzUJfan8z9sFE4QCzjjzlCZmuJN+aOkH0= -github.com/ultraware/funlen v0.0.1/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= -github.com/ultraware/funlen v0.0.2 h1:Av96YVBwwNSe4MLR7iI/BIa3VyI7/djnto/pK3Uxbdo= -github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= -github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= @@ -516,9 +347,6 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d h1:adrbvkTDn9rGnXg2IJDKozEpXXLZN89pdIA+Syt4/u0= -golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -529,12 +357,9 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -544,10 +369,6 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190514140710-3ec191127204 h1:4yG6GqBtw9C+UrLp6s2wtSniayy/Vd/3F7ffLE427XI= -golang.org/x/net v0.0.0-20190514140710-3ec191127204/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -563,7 +384,6 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -574,21 +394,16 @@ golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dzugGxJ/SQHoNufZJq1w= -golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190621203818-d432491b9138/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b h1:3S2h5FadpNr0zUUCVZjlKIEYF+KaX/OBplTGo89CYHI= -golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5 h1:SW/0nsKCUaozCUtZTakri5laocGx/5bkDSSLrFUsa5s= golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -599,32 +414,21 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuA golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138 h1:H3uGjxCR/6Ds0Mjgyp7LMK81+LvmbvWWEnJhzk1Pi9E= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190501045030-23463209683d h1:D7DVZUZEUgsSIDTivnUtVeGfN5AvhDIKtdIZAqx0ieE= golang.org/x/tools v0.0.0-20190501045030-23463209683d/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190909030654-5b82db07426d h1:PhtdWYteEBebOX7KXm4qkIAVSUTHQ883/2hRB92r9lk= golang.org/x/tools v0.0.0-20190909030654-5b82db07426d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911200332-7dd31af5a648 h1:mMUDZQR5S04OXjCHlt6FdLi8llhz1KChceYLfK4+sL0= -golang.org/x/tools v0.0.0-20190911200332-7dd31af5a648/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= @@ -645,16 +449,13 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v9 v9.27.0 h1:wCg/0hk9RzcB0CYw8pYV6FiBYug1on0cpco9YZF8jqA= @@ -694,40 +495,22 @@ k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.3 h1:niceAagH1tzskmaie/icWd7ci1wbG7Bf2c6YGcQv+3c= k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/kube-openapi v0.0.0-20180731170545-e3762e86a74c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= -k8s.io/kube-openapi v0.0.0-20190510232812-a01b7d5d6c22 h1:f0BTap/vrgs21vVbJ1ySdsNtcivpA1x4ut6Wla9HKKw= -k8s.io/kube-openapi v0.0.0-20190510232812-a01b7d5d6c22/go.mod h1:iU+ZGYsNlvU9XKUSso6SQfKTCCw7lFduMZy26Mgr2Fw= k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208 h1:5sW+fEHvlJI3Ngolx30CmubFulwH28DhKjGf70Xmtco= k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4= k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5 h1:VBM/0P5TWxwk+Nw6Z+lAw3DKgO76g90ETOiA6rfLV1Y= k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34 h1:duVSyluuJA+u0BnkcLR01smoLrGgDTfWt5c8ODYG8fU= -mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY= -mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4= -mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw= sigs.k8s.io/controller-runtime v0.2.0 h1:5gL30PXOisGZl+Osi4CmLhvMUj77BO3wJeouKF2va50= sigs.k8s.io/controller-runtime v0.2.0/go.mod h1:ZHqrRDZi3f6BzONcvlUxkqCKgwasGk5FZrnSv9TVZF4= sigs.k8s.io/controller-tools v0.2.0 h1:AmQ/0JKBJAjyAiPAkrAf9QW06jkx2lc5hpxMjamsFpw= sigs.k8s.io/controller-tools v0.2.0/go.mod h1:8t/X+FVWvk6TaBcsa+UKUBbn7GMtvyBKX30SGl4em6Y= -sigs.k8s.io/controller-tools v0.2.1 h1:HoCik83vXOpPi7KSJWdPRmiGntyOzK0v0BTV4U+pl8o= -sigs.k8s.io/kind v0.4.0 h1:C/QMxmF5Sp3yyKkTJ5+VJPcclZyu/JV8wLyihZbsHaI= -sigs.k8s.io/kind v0.4.0/go.mod h1:bgGo2cWxKGQ7esVxtGp9H17Ttlexju92CTMjCg08HNQ= sigs.k8s.io/kind v0.5.1 h1:BYnHEJ9DC+0Yjlyyehqd3xnKtEmFdLKU8QxqOqvQzdw= sigs.k8s.io/kind v0.5.1/go.mod h1:L+Kcoo83/D1+ryU5P2VFbvYm0oqbkJn9zTZq0KNxW68= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/kustomize/v3 v3.1.1-0.20190821175718-4b67a6de1296 h1:iQaIG5Dq+3qSiaFrJ/l/0MjjxKmdwyVNpKRYJwUe/+0= sigs.k8s.io/kustomize/v3 v3.1.1-0.20190821175718-4b67a6de1296/go.mod h1:ztX4zYc/QIww3gSripwF7TBOarBTm5BvyAMem0kCzOE= -sigs.k8s.io/structured-merge-diff v0.0.0-20190426204423-ea680f03cc65/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/testing_frameworks v0.1.1 h1:cP2l8fkA3O9vekpy5Ks8mmA0NW/F7yBdXf8brkWhVrs= sigs.k8s.io/testing_frameworks v0.1.1/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXAkZIk2RuE/7/FoK9maXw+TNPJhVS/c= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= -sourcegraph.com/sqs/pbtypes v1.0.0 h1:f7lAwqviDEGvON4kRv0o5V7FT/IQK+tbkF664XMbP3o= -sourcegraph.com/sqs/pbtypes v1.0.0/go.mod h1:3AciMUv4qUuRHRHhOG4TZOB+72GdPVz5k+c648qsFS4= diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index e9b54fb0a..713b1222a 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -17,8 +17,6 @@ package controller import ( "github.com/kudobuilder/kudo/pkg/controller/instance" - "github.com/kudobuilder/kudo/pkg/controller/operator" - "github.com/kudobuilder/kudo/pkg/controller/operatorversion" "github.com/kudobuilder/kudo/pkg/controller/planexecution" "sigs.k8s.io/controller-runtime/pkg/manager" ) @@ -27,8 +25,6 @@ import ( var AddControllerToManagerFuncs = []func(manager.Manager) error{ planexecution.Add, instance.Add, - operator.Add, - operatorversion.Add, } // AddControllersToManager adds all Controllers to the Manager diff --git a/pkg/controller/operator/operator_controller.go b/pkg/controller/operator/operator_controller.go index 7702af9b1..428589566 100644 --- a/pkg/controller/operator/operator_controller.go +++ b/pkg/controller/operator/operator_controller.go @@ -19,56 +19,26 @@ import ( "context" "log" - "k8s.io/client-go/tools/record" - kudov1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) -// Add creates a new Operator Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller -// and Start it when the Manager is Started. -func Add(mgr manager.Manager) error { - log.Printf("OperatorController: Registering operator controller.") - return add(mgr, newReconciler(mgr)) -} - -// newReconciler returns a new reconcile.Reconciler -func newReconciler(mgr manager.Manager) reconcile.Reconciler { - - return &ReconcileOperator{Client: mgr.GetClient(), scheme: mgr.GetScheme(), recorder: mgr.GetEventRecorderFor("operator-controller")} -} - -// add adds a new Controller to mgr with r as the reconcile.Reconciler -func add(mgr manager.Manager, r reconcile.Reconciler) error { - // Create a new controller - c, err := controller.New("operator-controller", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } - - // Watch for changes to Operator - err = c.Watch(&source.Kind{Type: &kudov1alpha1.Operator{}}, &handler.EnqueueRequestForObject{}) - if err != nil { - return err - } +var _ reconcile.Reconciler = &Reconciler{} - return nil +// Reconciler reconciles an Operator object +type Reconciler struct { + client.Client } -var _ reconcile.Reconciler = &ReconcileOperator{} - -// ReconcileOperator reconciles an Operator object -type ReconcileOperator struct { - client.Client - scheme *runtime.Scheme - recorder record.EventRecorder +// SetupWithManager registers this reconciler with the controller manager +func (r *Reconciler) SetupWithManager( + mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&kudov1alpha1.Operator{}). + Complete(r) } // Reconcile reads that state of the cluster for an Operator object and makes changes based on the state read @@ -76,7 +46,7 @@ type ReconcileOperator struct { // Automatically generate RBAC rules to allow the Controller to read and write Deployments // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=kudo.dev,resources=operators,verbs=get;list;watch;create;update;patch;delete -func (r *ReconcileOperator) Reconcile(request reconcile.Request) (reconcile.Result, error) { +func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { // Fetch the operator operator := &kudov1alpha1.Operator{} err := r.Get(context.TODO(), request.NamespacedName, operator) diff --git a/pkg/controller/operatorversion/operatorversion_controller.go b/pkg/controller/operatorversion/operatorversion_controller.go index 135e31f86..93667c561 100644 --- a/pkg/controller/operatorversion/operatorversion_controller.go +++ b/pkg/controller/operatorversion/operatorversion_controller.go @@ -21,50 +21,24 @@ import ( kudov1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) -// Add creates a new OperatorVersion Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller -// and Start it when the Manager is Started. -func Add(mgr manager.Manager) error { - log.Printf("OperatorVersionController: Registering operatorversion controller.") - return add(mgr, newReconciler(mgr)) -} - -// newReconciler returns a new reconcile.Reconciler. -func newReconciler(mgr manager.Manager) reconcile.Reconciler { - return &ReconcileOperatorVersion{Client: mgr.GetClient(), scheme: mgr.GetScheme()} -} - -// add adds a new Controller to mgr with r as the reconcile.Reconciler. -func add(mgr manager.Manager, r reconcile.Reconciler) error { - // Create a new controller - c, err := controller.New("operatorversion-controller", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } +var _ reconcile.Reconciler = &Reconciler{} - // Watch for changes to OperatorVersion - err = c.Watch(&source.Kind{Type: &kudov1alpha1.OperatorVersion{}}, &handler.EnqueueRequestForObject{}) - if err != nil { - return err - } - - return nil +// Reconciler reconciles an OperatorVersion object +type Reconciler struct { + client.Client } -var _ reconcile.Reconciler = &ReconcileOperatorVersion{} - -// ReconcileOperatorVersion reconciles an OperatorVersion object -type ReconcileOperatorVersion struct { - client.Client - scheme *runtime.Scheme +// SetupWithManager registers this reconciler with the controller manager +func (r *Reconciler) SetupWithManager( + mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&kudov1alpha1.OperatorVersion{}). + Complete(r) } // Reconcile reads that state of the cluster for an OperatorVersion object and makes changes based on the state read @@ -73,7 +47,7 @@ type ReconcileOperatorVersion struct { // Automatically generate RBAC rules to allow the Controller to read and write Deployments // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=kudo.dev,resources=operatorversions,verbs=get;list;watch;create;update;patch;delete -func (r *ReconcileOperatorVersion) Reconcile(request reconcile.Request) (reconcile.Result, error) { +func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { // Fetch the operator version operatorVersion := &kudov1alpha1.OperatorVersion{} err := r.Get(context.TODO(), request.NamespacedName, operatorVersion) diff --git a/pkg/test/harness.go b/pkg/test/harness.go index 2f0c3eca1..d61bf2be3 100644 --- a/pkg/test/harness.go +++ b/pkg/test/harness.go @@ -16,7 +16,6 @@ import ( kudo "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" "github.com/kudobuilder/kudo/pkg/controller" testutils "github.com/kudobuilder/kudo/pkg/test/utils" - "github.com/kudobuilder/kudo/pkg/webhook" "k8s.io/client-go/discovery" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -252,10 +251,6 @@ func (h *Harness) RunKUDO() error { return err } - if err = webhook.AddToManager(mgr); err != nil { - return err - } - h.managerStopCh = make(chan struct{}) go mgr.Start(h.managerStopCh) diff --git a/pkg/webhook/webhook.go b/pkg/webhook/webhook.go deleted file mode 100644 index 4cb65871c..000000000 --- a/pkg/webhook/webhook.go +++ /dev/null @@ -1,36 +0,0 @@ -/* - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package webhook - -import ( - "sigs.k8s.io/controller-runtime/pkg/manager" -) - -// AddToManagerFuncs is a list of functions to add all Controllers to the Manager -var AddToManagerFuncs []func(manager.Manager) error - -// AddToManager adds all Controllers to the Manager -// +kubebuilder:rbac:groups=admissionregistration.k8s.io,resources=mutatingwebhookconfigurations;validatingwebhookconfigurations,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete -func AddToManager(m manager.Manager) error { - for _, f := range AddToManagerFuncs { - if err := f(m); err != nil { - return err - } - } - return nil -} From 7a9e240002b1d5c153fff5126f8b2db84cbcb495 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Tue, 17 Sep 2019 13:58:35 +0200 Subject: [PATCH 02/29] Formatting --- cmd/manager/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 0cea36775..5f6abac77 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -17,9 +17,10 @@ package main import ( "fmt" - "github.com/kudobuilder/kudo/pkg/controller/operatorversion" "os" + "github.com/kudobuilder/kudo/pkg/controller/operatorversion" + "github.com/kudobuilder/kudo/pkg/controller/operator" "k8s.io/apimachinery/pkg/runtime" From 639e55e8bc0c8acc9b482648e61a0dc3f6b4213e Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Tue, 17 Sep 2019 15:16:11 +0200 Subject: [PATCH 03/29] More cleanup --- pkg/controller/operator/operator_controller.go | 2 -- pkg/controller/operatorversion/operatorversion_controller.go | 2 -- 2 files changed, 4 deletions(-) diff --git a/pkg/controller/operator/operator_controller.go b/pkg/controller/operator/operator_controller.go index 428589566..97f9c41e1 100644 --- a/pkg/controller/operator/operator_controller.go +++ b/pkg/controller/operator/operator_controller.go @@ -26,8 +26,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -var _ reconcile.Reconciler = &Reconciler{} - // Reconciler reconciles an Operator object type Reconciler struct { client.Client diff --git a/pkg/controller/operatorversion/operatorversion_controller.go b/pkg/controller/operatorversion/operatorversion_controller.go index 93667c561..c1846e57d 100644 --- a/pkg/controller/operatorversion/operatorversion_controller.go +++ b/pkg/controller/operatorversion/operatorversion_controller.go @@ -26,8 +26,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -var _ reconcile.Reconciler = &Reconciler{} - // Reconciler reconciles an OperatorVersion object type Reconciler struct { client.Client From eb920c988440f4424f4fc00e3ab1e6bb4a376451 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Tue, 17 Sep 2019 16:08:12 +0200 Subject: [PATCH 04/29] Skeleton of the new instance controller --- .../instance2/instance_controller.go | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 pkg/controller/instance2/instance_controller.go diff --git a/pkg/controller/instance2/instance_controller.go b/pkg/controller/instance2/instance_controller.go new file mode 100644 index 000000000..9b51bb2cf --- /dev/null +++ b/pkg/controller/instance2/instance_controller.go @@ -0,0 +1,63 @@ +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package instance + +import ( + "k8s.io/client-go/tools/record" + + kudov1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" + appsv1 "k8s.io/api/apps/v1" + batchv1 "k8s.io/api/batch/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +// Reconciler reconciles an Instance object. +type Reconciler struct { + client.Client + recorder record.EventRecorder +} + +// SetupWithManager registers this reconciler with the controller manager +func (r *Reconciler) SetupWithManager( + mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&kudov1alpha1.Instance{}). + Owns(&kudov1alpha1.Instance{}). + Owns(&appsv1.Deployment{}). + Owns(&batchv1.Job{}). + Owns(&appsv1.StatefulSet{}). + Complete(r) +} + +// Reconcile ... +// +// Automatically generate RBAC rules to allow the Controller to read and write Deployments +// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=kudo.dev,resources=instances,verbs=get;list;watch;create;update;patch;delete +func (r *Reconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) { + // ---------- 1. Query the current state ---------- + + // query instance + // query OV + + // ---------- 2. If we're currently running plan, continue with the execution ---------- + + // ---------- 3. Update status of instance after the execution proceeded ---------- + + return reconcile.Result{}, nil +} From f894bfdd0372af7f3a3cd40928458bd9a8733196 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Tue, 17 Sep 2019 17:08:08 +0200 Subject: [PATCH 05/29] Progress --- .../instance2/instance_controller.go | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/pkg/controller/instance2/instance_controller.go b/pkg/controller/instance2/instance_controller.go index 9b51bb2cf..607587620 100644 --- a/pkg/controller/instance2/instance_controller.go +++ b/pkg/controller/instance2/instance_controller.go @@ -16,6 +16,9 @@ limitations under the License. package instance import ( + "context" + "log" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/client-go/tools/record" kudov1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" @@ -49,10 +52,14 @@ func (r *Reconciler) SetupWithManager( // Automatically generate RBAC rules to allow the Controller to read and write Deployments // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=kudo.dev,resources=instances,verbs=get;list;watch;create;update;patch;delete -func (r *Reconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) { +func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { // ---------- 1. Query the current state ---------- - // query instance + log.Printf("InstanceController: Received Reconcile request for instance \"%+v\"", request.Name) + instance, err := r.getInstance(request) + if err != nil { + return reconcile.Result{}, err + } // query OV // ---------- 2. If we're currently running plan, continue with the execution ---------- @@ -61,3 +68,20 @@ func (r *Reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err return reconcile.Result{}, nil } + +// getInstance retrieves the instance by namespaced name +// returns nil, nil when instance is not found (not found is not considered an error) +func (r *Reconciler) getInstance(request ctrl.Request) (instance *kudov1alpha1.Instance, err error) { + instance = &kudov1alpha1.Instance{} + err = r.Get(context.TODO(), request.NamespacedName, instance) + if err != nil { + if errors.IsNotFound(err) { + // Object not found, return. Created objects are automatically garbage collected. + // For additional cleanup logic use finalizers. + return nil, nil + } + // Error reading the object - requeue the request. + return nil, err + } + return instance, nil +} \ No newline at end of file From ea1c146037a574fc48d19b14a473da36cd8db54b Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Fri, 20 Sep 2019 21:39:48 +0200 Subject: [PATCH 06/29] Implement plan execution in instance controller --- pkg/apis/kudo/v1alpha1/instance_types.go | 98 ++-- pkg/apis/kudo/v1alpha1/planexecution_types.go | 91 --- .../kudo/v1alpha1/zz_generated.deepcopy.go | 138 +---- .../kudo/v1alpha1/fake/fake_kudo_client.go | 4 - .../kudo/v1alpha1/generated_expansion.go | 2 - .../typed/kudo/v1alpha1/kudo_client.go | 5 - .../informers/externalversions/generic.go | 2 - .../kudo/v1alpha1/interface.go | 7 - .../kudo/v1alpha1/expansion_generated.go | 8 - .../apply_conventions.go | 2 +- .../instance/instance_controller.go | 538 +++++------------- .../plan_execution_engine.go | 86 +-- .../plan_execution_engine_test.go | 54 +- .../instance2/instance_controller.go | 87 --- .../planexecution/planexecution_controller.go | 480 ---------------- pkg/util/health/ready.go | 20 +- 16 files changed, 311 insertions(+), 1311 deletions(-) delete mode 100644 pkg/apis/kudo/v1alpha1/planexecution_types.go rename pkg/controller/{planexecution => instance}/apply_conventions.go (99%) rename pkg/controller/{planexecution => instance}/plan_execution_engine.go (83%) rename pkg/controller/{planexecution => instance}/plan_execution_engine_test.go (67%) delete mode 100644 pkg/controller/instance2/instance_controller.go delete mode 100644 pkg/controller/planexecution/planexecution_controller.go diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index a89ecdcd3..f451456c9 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -22,59 +22,79 @@ import ( // InstanceSpec defines the desired state of Instance. type InstanceSpec struct { - // Operator specifies a reference to a specific Operator object. + // OperatorVersion specifies a reference to a specific Operator object. OperatorVersion corev1.ObjectReference `json:"operatorVersion,omitempty"` - Dependencies []OperatorDependency `json:"dependencies,omitempty"` // TODO: this is deprecated and should not be used - Parameters map[string]string `json:"parameters,omitempty"` + Parameters map[string]string `json:"parameters,omitempty"` } // InstanceStatus defines the observed state of Instance type InstanceStatus struct { - // TODO turn into struct - ActivePlan corev1.ObjectReference `json:"activePlan,omitempty"` - Status PhaseState `json:"status,omitempty"` + PlanStatus map[string]PlanStatus `json:"planStatus,omitempty"` + AggregatedStatus AggregatedStatus `json:"aggregatedStatus,omitempty"` } -/* -Using Kubernetes cluster: kubernetes-cluster1 -deploy (serial strategy) (IN_PROGRESS) -├─ etcd (serial strategy) (STARTED) -│ ├─ etcd-0:[peer] (STARTED) -│ ├─ etcd-1:[peer] (PENDING) -│ └─ etcd-2:[peer] (PENDING) -├─ control-plane (dependency strategy) (PENDING) -│ ├─ kube-control-plane-0:[instance] (PENDING) -│ ├─ kube-control-plane-1:[instance] (PENDING) -│ └─ kube-control-plane-2:[instance] (PENDING) -├─ mandatory-addons (serial strategy) (PENDING) -│ └─ mandatory-addons-0:[instance] (PENDING) -├─ node (dependency strategy) (PENDING) -│ ├─ kube-node-0:[kubelet] (PENDING) -│ ├─ kube-node-1:[kubelet] (PENDING) -│ ├─ kube-node-2:[kubelet] (PENDING) -│ ├─ kube-node-3:[kubelet] (PENDING) -│ └─ kube-node-4:[kubelet] (PENDING) -└─ public-node (dependency strategy) (COMPLETE) -*/ +// AggregatedStatus is overview of an instance status derived from the plan status +type AggregatedStatus struct { + Status ExecutionStatus `json:"status,omitempty"` + ActivePlanName string `json:"activePlanName,omitempty"` +} + +// PlanStatus is representing status of a plan +type PlanStatus struct { + Name string `json:"name,omitempty"` + Status ExecutionStatus `json:"status,omitempty"` + LastFinishedRun metav1.Time `json:"lastFinishedRun,omitempty"` + Phases []PhaseStatus `json:"phases,omitempty"` +} + +// PhaseStatus is representing status of a phase +type PhaseStatus struct { + Name string `json:"name,omitempty"` + Status ExecutionStatus `json:"state,omitempty"` + Steps []StepStatus `json:"steps,omitempty"` +} + +// StepStatus is representing status of a step +type StepStatus struct { + Name string `json:"name,omitempty"` + Status ExecutionStatus `json:"state,omitempty"` +} + +// ExecutionStatus captures the state of the rollout. +type ExecutionStatus string + +// ExecutionInProgress actively deploying, but not yet healthy. +const ExecutionInProgress ExecutionStatus = "IN_PROGRESS" + +// ExecutionPending Not ready to deploy because dependent phases/steps not healthy. +const ExecutionPending ExecutionStatus = "PENDING" -// PhaseState captures the state of the rollout. -type PhaseState string +// ExecutionComplete deployed and healthy. +const ExecutionComplete ExecutionStatus = "COMPLETE" -// PhaseStateInProgress actively deploying, but not yet healthy. -const PhaseStateInProgress PhaseState = "IN_PROGRESS" +// ExecutionError there was an error deploying the application. +const ExecutionError ExecutionStatus = "ERROR" -// PhaseStatePending Not ready to deploy because dependent phases/steps not healthy. -const PhaseStatePending PhaseState = "PENDING" +// ExecutionError there was an error deploying the application. +const ExecutionFatalError ExecutionStatus = "FATAL_ERROR" -// PhaseStateComplete deployed and healthy. -const PhaseStateComplete PhaseState = "COMPLETE" +func (s ExecutionStatus) IsTerminal() bool { + return s == ExecutionComplete || s == ExecutionFatalError +} -// PhaseStateError there was an error deploying the application. -const PhaseStateError PhaseState = "ERROR" +func (s ExecutionStatus) IsRunning() bool { + return s == ExecutionInProgress || s == ExecutionPending || s == ExecutionError +} -// PhaseStateSuspend Spec was triggered to stop this plan execution. -const PhaseStateSuspend PhaseState = "SUSPEND" +func (i *Instance) GetPlanInProgress() *PlanStatus { + for _, p := range i.Status.PlanStatus { + if p.Status.IsRunning() { + return &p + } + } + return nil +} // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/kudo/v1alpha1/planexecution_types.go b/pkg/apis/kudo/v1alpha1/planexecution_types.go deleted file mode 100644 index 3738aade5..000000000 --- a/pkg/apis/kudo/v1alpha1/planexecution_types.go +++ /dev/null @@ -1,91 +0,0 @@ -/* - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// PlanExecutionSpec defines the desired state of PlanExecution. -type PlanExecutionSpec struct { - // This flag tells the controller to suspend subsequent executions, it does - // not apply to already started executions. Defaults to false. - // +optional - Suspend *bool `json:"suspend,omitempty" protobuf:"varint,4,opt,name=suspend"` - - PlanName string `json:"planName"` - Instance corev1.ObjectReference `json:"instance"` - Template InstanceSpec `json:"template"` -} - -// PlanExecutionStatus defines the observed state of PlanExecution -type PlanExecutionStatus struct { - Name string `json:"name,omitempty"` - Strategy Ordering `json:"strategy,omitempty"` - State PhaseState `json:"state,omitempty"` - - // Phases maps a phase name to a Phase object - Phases []PhaseStatus `json:"phases,omitempty"` -} - -// PhaseStatus specifies the status of list of steps that contain Kubernetes objects. -type PhaseStatus struct { - Name string `json:"name,omitempty"` - Strategy Ordering `json:"strategy,omitempty"` - State PhaseState `json:"state,omitempty"` - - // Steps maps a step name to a list of templated Kubernetes objects stored as a string. - Steps []StepStatus `json:"steps"` -} - -// StepStatus shows the status of the Step. -type StepStatus struct { - Name string `json:"name,omitempty"` - State PhaseState `json:"state,omitempty"` - Delete bool `json:"delete,omitempty"` - - // Objects will be serialized for each instance as the params and defaults - // are provided, but not serialized in the payload. - Objects []runtime.Object `json:"-"` -} - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// PlanExecution is the Schema for the planexecutions API. -// +k8s:openapi-gen=true -type PlanExecution struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec PlanExecutionSpec `json:"spec,omitempty"` - Status PlanExecutionStatus `json:"status,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// PlanExecutionList contains a list of PlanExecution. -type PlanExecutionList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []PlanExecution `json:"items"` -} - -func init() { - SchemeBuilder.Register(&PlanExecution{}, &PlanExecutionList{}) -} diff --git a/pkg/apis/kudo/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/kudo/v1alpha1/zz_generated.deepcopy.go index 70ef45e6c..93d9b8b77 100644 --- a/pkg/apis/kudo/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/kudo/v1alpha1/zz_generated.deepcopy.go @@ -22,6 +22,22 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AggregatedStatus) DeepCopyInto(out *AggregatedStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AggregatedStatus. +func (in *AggregatedStatus) DeepCopy() *AggregatedStatus { + if in == nil { + return nil + } + out := new(AggregatedStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Command) DeepCopyInto(out *Command) { *out = *in @@ -143,7 +159,7 @@ func (in *Instance) DeepCopyInto(out *Instance) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) return } @@ -202,11 +218,6 @@ func (in *InstanceList) DeepCopyObject() runtime.Object { func (in *InstanceSpec) DeepCopyInto(out *InstanceSpec) { *out = *in out.OperatorVersion = in.OperatorVersion - if in.Dependencies != nil { - in, out := &in.Dependencies, &out.Dependencies - *out = make([]OperatorDependency, len(*in)) - copy(*out, *in) - } if in.Parameters != nil { in, out := &in.Parameters, &out.Parameters *out = make(map[string]string, len(*in)) @@ -230,7 +241,14 @@ func (in *InstanceSpec) DeepCopy() *InstanceSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstanceStatus) DeepCopyInto(out *InstanceStatus) { *out = *in - out.ActivePlan = in.ActivePlan + if in.PlanStatus != nil { + in, out := &in.PlanStatus, &out.PlanStatus + *out = make(map[string]PlanStatus, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + out.AggregatedStatus = in.AggregatedStatus return } @@ -615,9 +633,7 @@ func (in *PhaseStatus) DeepCopyInto(out *PhaseStatus) { if in.Steps != nil { in, out := &in.Steps, &out.Steps *out = make([]StepStatus, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } + copy(*out, *in) } return } @@ -656,92 +672,9 @@ func (in *Plan) DeepCopy() *Plan { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PlanExecution) DeepCopyInto(out *PlanExecution) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlanExecution. -func (in *PlanExecution) DeepCopy() *PlanExecution { - if in == nil { - return nil - } - out := new(PlanExecution) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PlanExecution) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PlanExecutionList) DeepCopyInto(out *PlanExecutionList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]PlanExecution, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlanExecutionList. -func (in *PlanExecutionList) DeepCopy() *PlanExecutionList { - if in == nil { - return nil - } - out := new(PlanExecutionList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PlanExecutionList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PlanExecutionSpec) DeepCopyInto(out *PlanExecutionSpec) { - *out = *in - if in.Suspend != nil { - in, out := &in.Suspend, &out.Suspend - *out = new(bool) - **out = **in - } - out.Instance = in.Instance - in.Template.DeepCopyInto(&out.Template) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlanExecutionSpec. -func (in *PlanExecutionSpec) DeepCopy() *PlanExecutionSpec { - if in == nil { - return nil - } - out := new(PlanExecutionSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PlanExecutionStatus) DeepCopyInto(out *PlanExecutionStatus) { +func (in *PlanStatus) DeepCopyInto(out *PlanStatus) { *out = *in + in.LastFinishedRun.DeepCopyInto(&out.LastFinishedRun) if in.Phases != nil { in, out := &in.Phases, &out.Phases *out = make([]PhaseStatus, len(*in)) @@ -752,12 +685,12 @@ func (in *PlanExecutionStatus) DeepCopyInto(out *PlanExecutionStatus) { return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlanExecutionStatus. -func (in *PlanExecutionStatus) DeepCopy() *PlanExecutionStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlanStatus. +func (in *PlanStatus) DeepCopy() *PlanStatus { if in == nil { return nil } - out := new(PlanExecutionStatus) + out := new(PlanStatus) in.DeepCopyInto(out) return out } @@ -1184,15 +1117,6 @@ func (in *Step) DeepCopy() *Step { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StepStatus) DeepCopyInto(out *StepStatus) { *out = *in - if in.Objects != nil { - in, out := &in.Objects, &out.Objects - *out = make([]runtime.Object, len(*in)) - for i := range *in { - if (*in)[i] != nil { - (*out)[i] = (*in)[i].DeepCopyObject() - } - } - } return } diff --git a/pkg/client/clientset/versioned/typed/kudo/v1alpha1/fake/fake_kudo_client.go b/pkg/client/clientset/versioned/typed/kudo/v1alpha1/fake/fake_kudo_client.go index afd6d8138..0ef88bf16 100644 --- a/pkg/client/clientset/versioned/typed/kudo/v1alpha1/fake/fake_kudo_client.go +++ b/pkg/client/clientset/versioned/typed/kudo/v1alpha1/fake/fake_kudo_client.go @@ -38,10 +38,6 @@ func (c *FakeKudoV1alpha1) OperatorVersions(namespace string) v1alpha1.OperatorV return &FakeOperatorVersions{c, namespace} } -func (c *FakeKudoV1alpha1) PlanExecutions(namespace string) v1alpha1.PlanExecutionInterface { - return &FakePlanExecutions{c, namespace} -} - // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeKudoV1alpha1) RESTClient() rest.Interface { diff --git a/pkg/client/clientset/versioned/typed/kudo/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/kudo/v1alpha1/generated_expansion.go index d9d50fef7..7afa3d569 100644 --- a/pkg/client/clientset/versioned/typed/kudo/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/kudo/v1alpha1/generated_expansion.go @@ -21,5 +21,3 @@ type InstanceExpansion interface{} type OperatorExpansion interface{} type OperatorVersionExpansion interface{} - -type PlanExecutionExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/kudo/v1alpha1/kudo_client.go b/pkg/client/clientset/versioned/typed/kudo/v1alpha1/kudo_client.go index da75b4f92..f01e5d84c 100644 --- a/pkg/client/clientset/versioned/typed/kudo/v1alpha1/kudo_client.go +++ b/pkg/client/clientset/versioned/typed/kudo/v1alpha1/kudo_client.go @@ -28,7 +28,6 @@ type KudoV1alpha1Interface interface { InstancesGetter OperatorsGetter OperatorVersionsGetter - PlanExecutionsGetter } // KudoV1alpha1Client is used to interact with features provided by the kudo.dev group. @@ -48,10 +47,6 @@ func (c *KudoV1alpha1Client) OperatorVersions(namespace string) OperatorVersionI return newOperatorVersions(c, namespace) } -func (c *KudoV1alpha1Client) PlanExecutions(namespace string) PlanExecutionInterface { - return newPlanExecutions(c, namespace) -} - // NewForConfig creates a new KudoV1alpha1Client for the given config. func NewForConfig(c *rest.Config) (*KudoV1alpha1Client, error) { config := *c diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index b23ec916a..d12f8a219 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -57,8 +57,6 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Kudo().V1alpha1().Operators().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("operatorversions"): return &genericInformer{resource: resource.GroupResource(), informer: f.Kudo().V1alpha1().OperatorVersions().Informer()}, nil - case v1alpha1.SchemeGroupVersion.WithResource("planexecutions"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Kudo().V1alpha1().PlanExecutions().Informer()}, nil } diff --git a/pkg/client/informers/externalversions/kudo/v1alpha1/interface.go b/pkg/client/informers/externalversions/kudo/v1alpha1/interface.go index 1f71cf912..30bb74ce0 100644 --- a/pkg/client/informers/externalversions/kudo/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/kudo/v1alpha1/interface.go @@ -28,8 +28,6 @@ type Interface interface { Operators() OperatorInformer // OperatorVersions returns a OperatorVersionInformer. OperatorVersions() OperatorVersionInformer - // PlanExecutions returns a PlanExecutionInformer. - PlanExecutions() PlanExecutionInformer } type version struct { @@ -57,8 +55,3 @@ func (v *version) Operators() OperatorInformer { func (v *version) OperatorVersions() OperatorVersionInformer { return &operatorVersionInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } - -// PlanExecutions returns a PlanExecutionInformer. -func (v *version) PlanExecutions() PlanExecutionInformer { - return &planExecutionInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} diff --git a/pkg/client/listers/kudo/v1alpha1/expansion_generated.go b/pkg/client/listers/kudo/v1alpha1/expansion_generated.go index b9e4b2a53..36477d24b 100644 --- a/pkg/client/listers/kudo/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/kudo/v1alpha1/expansion_generated.go @@ -39,11 +39,3 @@ type OperatorVersionListerExpansion interface{} // OperatorVersionNamespaceListerExpansion allows custom methods to be added to // OperatorVersionNamespaceLister. type OperatorVersionNamespaceListerExpansion interface{} - -// PlanExecutionListerExpansion allows custom methods to be added to -// PlanExecutionLister. -type PlanExecutionListerExpansion interface{} - -// PlanExecutionNamespaceListerExpansion allows custom methods to be added to -// PlanExecutionNamespaceLister. -type PlanExecutionNamespaceListerExpansion interface{} diff --git a/pkg/controller/planexecution/apply_conventions.go b/pkg/controller/instance/apply_conventions.go similarity index 99% rename from pkg/controller/planexecution/apply_conventions.go rename to pkg/controller/instance/apply_conventions.go index ec9870ec6..bc825058c 100644 --- a/pkg/controller/planexecution/apply_conventions.go +++ b/pkg/controller/instance/apply_conventions.go @@ -1,4 +1,4 @@ -package planexecution +package instance import ( "fmt" diff --git a/pkg/controller/instance/instance_controller.go b/pkg/controller/instance/instance_controller.go index 2511fa0bc..7974c37b7 100644 --- a/pkg/controller/instance/instance_controller.go +++ b/pkg/controller/instance/instance_controller.go @@ -19,391 +19,168 @@ import ( "context" "fmt" "log" - "reflect" - "time" + "strings" - "github.com/kudobuilder/kudo/pkg/util/kudo" + "k8s.io/apimachinery/pkg/runtime" + "github.com/kudobuilder/kudo/pkg/util/kudo" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" kudov1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" + appsv1 "k8s.io/api/apps/v1" + batchv1 "k8s.io/api/batch/v1" + ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/apiutil" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) -// Add creates a new Instance Controller and adds it to the Manager with default RBAC. -// -// The Manager will set fields on the Controller and start it when the Manager is started. -func Add(mgr manager.Manager) error { - log.Printf("InstanceController: Registering instance controller.") - return add(mgr, newReconciler(mgr)) +// Reconciler reconciles an Instance object. +type Reconciler struct { + client.Client + recorder record.EventRecorder + scheme *runtime.Scheme } -// newReconciler returns a new reconcile.Reconciler -func newReconciler(mgr manager.Manager) reconcile.Reconciler { - return &ReconcileInstance{Client: mgr.GetClient(), scheme: mgr.GetScheme(), recorder: mgr.GetEventRecorderFor("instance-controller")} +// SetupWithManager registers this reconciler with the controller manager +func (r *Reconciler) SetupWithManager( + mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&kudov1alpha1.Instance{}). + Owns(&kudov1alpha1.Instance{}). + Owns(&appsv1.Deployment{}). + Owns(&batchv1.Job{}). + Owns(&appsv1.StatefulSet{}). + Complete(r) } -// add adds a new Controller to mgr with r as the reconcile.Reconciler. -func add(mgr manager.Manager, r reconcile.Reconciler) error { - // Create a new controller - c, err := controller.New("instance-controller", mgr, controller.Options{Reconciler: r}) +// Reconcile ... +// +// Automatically generate RBAC rules to allow the Controller to read and write Deployments +// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=kudo.dev,resources=instances,verbs=get;list;watch;create;update;patch;delete +func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { + // ---------- 1. Query the current state ---------- + + log.Printf("InstanceController: Received Reconcile request for instance \"%+v\"", request.Name) + instance, err := r.getInstance(request) if err != nil { - return err + if apierrors.IsNotFound(err) { // not retrying if instance not found, probably someone manually removed it? + return reconcile.Result{}, nil + } + return reconcile.Result{}, err } - // Watch for changes to Instance - if err = c.Watch(&source.Kind{Type: &kudov1alpha1.Instance{}}, &handler.EnqueueRequestForObject{}, instanceEventFilter(mgr)); err != nil { - return err + ov, err := r.getOperatorVersion(instance) + if err != nil { + return reconcile.Result{}, err // OV not found has to be retried because it can really have been created after I } - // Watch for changes to OperatorVersion. Since changes to OperatorVersion and Instance are often happening - // concurrently there is an inherent race between both update events so that we might see a new Instance first - // without the corresponding OperatorVersion. We additionally watch OperatorVersions and trigger - // reconciliation for the corresponding instances. - // - // Define a mapping from the object in the event (OperatorVersion) to one or more objects to - // reconcile (Instances). Specifically this calls for a reconciliation of any owned objects. - ovEventHandler := handler.ToRequestsFunc( - func(a handler.MapObject) []reconcile.Request { - requests := make([]reconcile.Request, 0) - // We want to query and queue up operators Instances - instances := &kudov1alpha1.InstanceList{} - // we are listing all instances here, which could come with some performance penalty - // a possible optimization is to introduce filtering based on operatorversion (or operator) - err := mgr.GetClient().List( - context.TODO(), - instances, - ) - - if err != nil { - log.Printf("InstanceController: Error fetching instances list for operator %v: %v", a.Meta.GetName(), err) - return nil - } - - for _, instance := range instances.Items { - // Sanity check - lets make sure that this instance references the operatorVersion - if instance.Spec.OperatorVersion.Name == a.Meta.GetName() && - instance.GetOperatorVersionNamespace() == a.Meta.GetNamespace() && - instance.Status.ActivePlan.Name == "" { - - log.Printf("InstanceController: Creating a deploy execution plan for the instance %v", instance.Name) - err = createPlanAndUpdateReference(mgr.GetClient(), mgr.GetEventRecorderFor("instance-controller"), mgr.GetScheme(), "deploy", &instance) - if err != nil { - log.Printf("InstanceController: Error creating \"%v\" object for \"deploy\": %v", instance.Name, err) - } - - log.Printf("InstanceController: Queing instance %v for reconciliation", instance.Name) - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Name: instance.Name, - Namespace: instance.Namespace, - }, - }) - } - } - log.Printf("InstanceController: Found %v instances to reconcile for operator %v", len(requests), a.Meta.GetName()) - return requests - }) - - // This map function makes sure that we *ONLY* handle created operatorVersion - ovEventFilter := predicate.Funcs{ - CreateFunc: func(e event.CreateEvent) bool { - log.Printf("InstanceController: Received create event for: %v", e.Meta.GetName()) - return true - }, - UpdateFunc: func(e event.UpdateEvent) bool { - return false - }, - DeleteFunc: func(e event.DeleteEvent) bool { - return false - }, - } + // ---------- 2. First check if we should not start execution of new plan because of update ---------- - if err = c.Watch(&source.Kind{Type: &kudov1alpha1.OperatorVersion{}}, &handler.EnqueueRequestsFromMapFunc{ToRequests: ovEventHandler}, ovEventFilter); err != nil { - return err - } + // TODO - return nil -} + // ---------- 3. If we're currently running plan, continue with the execution ---------- -func instanceEventFilter(mgr manager.Manager) predicate.Funcs { - ctx := context.TODO() - return predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - old := e.ObjectOld.(*kudov1alpha1.Instance) - new := e.ObjectNew.(*kudov1alpha1.Instance) - - // Get the OperatorVersion that corresponds to the new instance. - ov := &kudov1alpha1.OperatorVersion{} - err := mgr.GetClient().Get(ctx, - types.NamespacedName{ - Name: new.Spec.OperatorVersion.Name, - Namespace: new.GetOperatorVersionNamespace(), - }, - ov) - if err != nil { - log.Printf("InstanceController: Error getting operatorversion \"%v\" for instance \"%v\": %v", - new.Spec.OperatorVersion.Name, - new.Name, - err) - // TODO: We probably want to handle this differently and mark this instance as unhealthy - // since it's linking to a bad OV. - return false - } - - // Identify plan to be executed by this change. - var planName string - var planFound bool - - if old.Spec.OperatorVersion != new.Spec.OperatorVersion { - // It's an upgrade! - names := []string{"upgrade", "update", "deploy"} - for _, n := range names { - if _, planFound = ov.Spec.Plans[n]; planFound { - planName = n - break - } - } - - if !planFound { - log.Printf("InstanceController: Could not find any plan to use to upgrade instance %v", new.Name) - return false - } - } else if !reflect.DeepEqual(old.Spec, new.Spec) { - for k := range parameterDifference(old.Spec.Parameters, new.Spec.Parameters) { - // Find the spec of the updated parameter. - paramFound := false - for _, param := range ov.Spec.Parameters { - if param.Name == k { - paramFound = true - - if param.Trigger != "" { - planName = param.Trigger - planFound = true - } - - break - } - } - - if paramFound { - if !planFound { - // The parameter doesn't have a trigger, try to find the corresponding default plan. - names := []string{"update", "deploy"} - for _, n := range names { - if _, planFound = ov.Spec.Plans[n]; planFound { - planName = n - planFound = true - break - } - } - - if planFound { - log.Printf("InstanceController: Instance %v updated parameter %v, but it is not associated to a trigger. Using default plan %v\n", new.Name, k, planName) - } - } - - if !planFound { - log.Printf("InstanceController: Could not find any plan to use to update instance %v", new.Name) - } - } else { - log.Printf("InstanceController: Instance %v updated parameter %v, but parameter not found in operatorversion %v\n", new.Name, k, ov.Name) - } - } - } else { - // FIXME: reading the status here feels very wrong, and so does the fact - // that this predicate funcion has side effects. We could probably move - // all this logic to the reconciler if we stored the parameters in the - // `PlanExecution`. - // See https://github.com/kudobuilder/kudo/issues/422 - if new.Status.ActivePlan.Name == "" { - log.Printf("InstanceController: Old and new spec matched...\n %+v ?= %+v\n", old.Spec, new.Spec) - planName = "deploy" - planFound = true - } - } - - if planFound { - log.Printf("InstanceController: Going to run plan \"%v\" for instance %v", planName, new.Name) - // Suspend the the current plan. - current := &kudov1alpha1.PlanExecution{} - err = mgr.GetClient().Get(ctx, client.ObjectKey{Name: new.Status.ActivePlan.Name, Namespace: new.Status.ActivePlan.Namespace}, current) - if err != nil { - log.Printf("InstanceController: Ignoring error when getting plan for new instance: %v", err) - } else { - if current.Status.State == kudov1alpha1.PhaseStateComplete { - log.Printf("InstanceController: Current plan for instance %v is already done, won't change the Suspend flag.", new.Name) - } else { - log.Printf("InstanceController: Suspending the PlanExecution for instance %v", new.Name) - t := true - current.Spec.Suspend = &t - did, err := controllerutil.CreateOrUpdate(ctx, mgr.GetClient(), current, func() error { - t := true - current.Spec.Suspend = &t - return nil - }) - if err != nil { - log.Printf("InstanceController: Error suspending PlanExecution for instance %v: %v", new.Name, err) - } else { - log.Printf("InstanceController: Successfully suspended PlanExecution for instance %v. Returned: %v", new.Name, did) - } - } - } - - if err = createPlanAndUpdateReference(mgr.GetClient(), mgr.GetEventRecorderFor("instance-controller"), mgr.GetScheme(), planName, new); err != nil { - log.Printf("InstanceController: Error creating PlanExecution \"%v\" for instance \"%v\": %v", planName, new.Name, err) - } - } - - // See if there's a current plan being run, if so "cancel" the plan run. - return e.ObjectOld != e.ObjectNew - }, - // New Instances should confirm there is a deployment plan without side-effects) - CreateFunc: func(e event.CreateEvent) bool { - log.Printf("InstanceController: Received create event for instance \"%v\"", e.Meta.GetName()) - ctx := context.TODO() - - // In order to create we must have an OV and it must have a "deploy" plan. - instance := e.Object.(*kudov1alpha1.Instance) - - ov, err := getOperatorVersion(ctx, mgr.GetClient(), nil, instance) - if err != nil || ov == nil { - log.Printf("InstanceController: Error getting operatorversion \"%v\" for instance \"%v\": %v", - instance.Spec.OperatorVersion.Name, - instance.Name, - err) - // TODO: We probably want to handle this differently and mark this instance as unhealthy - // no ov, no create of instance - return false - } - - planName := "deploy" - if _, ok := ov.Spec.Plans[planName]; !ok { - log.Printf("InstanceController: Could not find deploy plan \"%v\" for instance \"%v\"", planName, instance.Name) - return false - } - return true - }, - DeleteFunc: func(e event.DeleteEvent) bool { - log.Printf("InstanceController: Received delete event for instance \"%v\"", e.Meta.GetName()) - return true - }, + activePlanStatus := instance.GetPlanInProgress() + if activePlanStatus == nil { // we have plan in progress + return reconcile.Result{}, nil } -} - -func createPlanAndUpdateReference(c client.Client, r record.EventRecorder, scheme *runtime.Scheme, planName string, instance *kudov1alpha1.Instance) error { - ctx := context.TODO() - planExecution, err := newPlanExecution(instance, planName, scheme) + activePlan, metadata, err := preparePlanExecution(instance, ov, activePlanStatus) if err != nil { - log.Printf("InstanceController: Error creating PlanExecution") - return err + err = r.handleError(err, instance) + return reconcile.Result{}, err } + newStatus, err := executePlan(activePlan, metadata, r.Client, &kustomizeEnhancer{r.scheme}) - log.Printf("Creating PlanExecution of planExecution %s for instance %s", planName, instance.Name) - r.Event(instance, "Normal", "CreatePlanExecution", fmt.Sprintf("Creating \"%v\" planExecution on %s", planName, instance.Name)) + // ---------- 4. Update status of instance after the execution proceeded ---------- - // Make this instance the owner of the PlanExecution - if err := controllerutil.SetControllerReference(instance, planExecution, scheme); err != nil { - log.Printf("InstanceController: Error setting ControllerReference") - return err + if newStatus != nil { + instance.Status.PlanStatus[activePlan.Name] = *newStatus } - if err := c.Create(ctx, planExecution); err != nil { - log.Printf("InstanceController: Error creating planexecution \"%v\": %v", planExecution.Name, err) - r.Event(instance, "Warning", "CreatePlanExecution", fmt.Sprintf("Error creating planexecution \"%v\": %v", planExecution.Name, err)) - return err + if err != nil { + err = r.handleError(err, instance) + return reconcile.Result{}, err } - log.Printf("Created PlanExecution of planExecution %s for instance %s", planName, instance.Name) - r.Event(instance, "Normal", "PlanCreated", fmt.Sprintf("PlanExecution \"%v\" created", planExecution.Name)) - - return addActivePlanReference(c, r, planExecution, instance) -} -func addActivePlanReference(c client.Client, r record.EventRecorder, planExecution *kudov1alpha1.PlanExecution, instance *kudov1alpha1.Instance) error { - instance.Status.ActivePlan = corev1.ObjectReference{ - Name: planExecution.Name, - Kind: "PlanExecution", - Namespace: planExecution.Namespace, - APIVersion: "kudo.dev/v1alpha1", - UID: planExecution.UID, - } - err := c.Update(context.TODO(), instance) + err = r.Client.Update(context.TODO(), instance) if err != nil { - r.Event(instance, "Warning", "UpdateError", fmt.Sprintf("Could not update the ActivePlan for (%v): %v", planExecution.Spec.Instance.Name, err)) + log.Printf("InstanceController: Error when updating instance state. %v", err) + return reconcile.Result{}, err } - return err -} - -var _ reconcile.Reconciler = &ReconcileInstance{} -// ReconcileInstance reconciles an Instance object. -type ReconcileInstance struct { - client.Client - scheme *runtime.Scheme - recorder record.EventRecorder + return reconcile.Result{}, nil } -// Reconcile reads that state of the cluster for an Instance object and makes changes based on the state read -// and what is in the Instance.Spec. -// -// Automatically generate RBAC rules to allow the Controller to read and write Deployments -// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=kudo.dev,resources=instances,verbs=get;list;watch;create;update;patch;delete -func (r *ReconcileInstance) Reconcile(request reconcile.Request) (reconcile.Result, error) { - ctx := context.TODO() - // Fetch the Instance instance - // Fetch the Instance instance - instance, err := getInstance(ctx, r, request) - if err != nil || instance == nil { - return reconcile.Result{}, err +func preparePlanExecution(instance *kudov1alpha1.Instance, ov *kudov1alpha1.OperatorVersion, activePlanStatus *kudov1alpha1.PlanStatus) (*activePlan, *executionMetadata, error) { + params, err := getParameters(instance, ov) + if err != nil { + return nil, nil, err } - // Make sure the OperatorVersion is present - ov, err := getOperatorVersion(ctx, r, r.recorder, instance) - if err != nil || ov == nil { - return reconcile.Result{}, err + planSpec, ok := ov.Spec.Plans[activePlanStatus.Name] + if !ok { + return nil, nil, executionError{fmt.Errorf("Could not find required plan (%v)", activePlanStatus.Name), false, kudo.String("InvalidPlan")} } - log.Printf("InstanceController: Received Reconcile request for instance \"%+v\"", request.Name) + return &activePlan{ + Name: activePlanStatus.Name, + Spec: &planSpec, + PlanStatus: activePlanStatus, + Tasks: ov.Spec.Tasks, + Templates: ov.Spec.Templates, + params: params, + }, &executionMetadata{ + operatorVersionName: ov.Name, + operatorVersion: ov.Spec.Version, + resourcesOwner: instance, + operatorName: ov.Spec.Operator.Name, + instanceNamespace: instance.Namespace, + instanceName: instance.Name, + }, nil +} - // if this is new create and create and assign a planexecution and return - if isNewInstance(instance) { - err = createPlanAndUpdateReference(r.Client, r.recorder, r.scheme, "deploy", instance) - // err or not we return. If err == nil the controller is done, else requeue - return reconcile.Result{}, err - } +// handleError handles execution error by logging, updating the plan status and optionally publishing an event +// specify eventReason as nil if you don't wish to publish a warning event +// returns err if this err should be retried, nil otherwise +func (r *Reconciler) handleError(err error, instance *kudov1alpha1.Instance) error { + // TODO update the execution state + log.Printf("InstanceController: %v", err) + if exErr, ok := err.(*executionError); ok { + if exErr.eventName != nil { + r.recorder.Event(instance, "Warning", kudo.StringValue(exErr.eventName), err.Error()) + } - // Make sure all the required parameters in the operatorVersion are present - for _, param := range ov.Spec.Parameters { - if param.Required && param.Default == nil { - if _, ok := instance.Spec.Parameters[param.Name]; !ok { - r.recorder.Event(instance, "Warning", "MissingParameter", fmt.Sprintf("Missing parameter \"%v\" required by operatorversion \"%v\"", param.Name, ov.Name)) - } + if exErr.fatal { + return nil // not retrying fatal error } } + return err +} - // Defer call from above should apply the status changes to the object - return reconcile.Result{}, nil +// getInstance retrieves the instance by namespaced name +// returns nil, nil when instance is not found (not found is not considered an error) +func (r *Reconciler) getInstance(request ctrl.Request) (instance *kudov1alpha1.Instance, err error) { + instance = &kudov1alpha1.Instance{} + err = r.Get(context.TODO(), request.NamespacedName, instance) + if err != nil { + // Error reading the object - requeue the request. + log.Printf("InstanceController: Error getting instance \"%v\": %v", + instance.Name, + err) + r.recorder.Event(instance, "Warning", "InvalidInstance", fmt.Sprintf("Error getting instance \"%v\": %v", instance.Name, err)) + return nil, err + } + return instance, nil } -// getOperatorVersionFromNameSpacedName does the work of getting an OV from a namespaced name in an instance. It is possible to pass a recorder as nil if an instance does not exist yet. -func getOperatorVersion(ctx context.Context, c client.Client, r record.EventRecorder, instance *kudov1alpha1.Instance) (ov *kudov1alpha1.OperatorVersion, err error) { +// getOperatorVersion retrieves operatorversion belonging to the given instance +// not found is treated here as any other error +func (r *Reconciler) getOperatorVersion(instance *kudov1alpha1.Instance) (ov *kudov1alpha1.OperatorVersion, err error) { ov = &kudov1alpha1.OperatorVersion{} - err = c.Get(ctx, + err = r.Get(context.TODO(), types.NamespacedName{ Name: instance.Spec.OperatorVersion.Name, Namespace: instance.GetOperatorVersionNamespace(), @@ -414,36 +191,37 @@ func getOperatorVersion(ctx context.Context, c client.Client, r record.EventReco instance.Spec.OperatorVersion.Name, instance.Name, err) - if r != nil { - r.Event(instance, "Warning", "InvalidOperatorVersion", fmt.Sprintf("Error getting operatorversion \"%v\": %v", ov.Name, err)) - } + r.recorder.Event(instance, "Warning", "InvalidOperatorVersion", fmt.Sprintf("Error getting operatorversion \"%v\": %v", ov.Name, err)) return nil, err } return ov, nil } -// getInstance retrieves the instance by namespace name -func getInstance(ctx context.Context, r *ReconcileInstance, request reconcile.Request) (instance *kudov1alpha1.Instance, err error) { - instance = &kudov1alpha1.Instance{} - err = r.Get(ctx, request.NamespacedName, instance) - if err != nil { - if errors.IsNotFound(err) { - // Object not found, return. Created objects are automatically garbage collected. - // For additional cleanup logic use finalizers. - return nil, nil +func getParameters(instance *kudov1alpha1.Instance, operatorVersion *kudov1alpha1.OperatorVersion) (map[string]string, error) { + params := make(map[string]string) + + for k, v := range instance.Spec.Parameters { + params[k] = v + } + + missingRequiredParameters := make([]string, 0) + // Merge defaults with customizations + for _, param := range operatorVersion.Spec.Parameters { + _, ok := params[param.Name] + if !ok && param.Required && param.Default == nil { + // instance does not define this parameter and there is no default while the parameter is required -> error + missingRequiredParameters = append(missingRequiredParameters, param.Name) + + } else if !ok { + params[param.Name] = kudo.StringValue(param.Default) } - // Error reading the object - requeue the request. - return nil, err } - log.Printf("InstanceController: Received Reconcile request for instance \"%+v\"", request.Name) - return instance, nil -} -// isNewInstance detects if the instance does NOT have plan -func isNewInstance(instance *kudov1alpha1.Instance) bool { - // if ActivePlan.Name is empty the instance is being created. The instance will forever - // after have an active plan (which may be complete) - return instance.Status.ActivePlan.Name == "" + if len(missingRequiredParameters) != 0 { + return nil, executionError{err: fmt.Errorf("parameters are missing when evaluating template: %s", strings.Join(missingRequiredParameters, ",")), fatal: true, eventName: kudo.String("Missing parameter")} + } + + return params, nil } func parameterDifference(old, new map[string]string) map[string]string { @@ -466,38 +244,16 @@ func parameterDifference(old, new map[string]string) map[string]string { return diff } -// newPlanExecution creates a PlanExecution based on the kind and instance for a given planName -func newPlanExecution(instance *kudov1alpha1.Instance, planName string, scheme *runtime.Scheme) (*kudov1alpha1.PlanExecution, error) { - gvk, err := apiutil.GVKForObject(instance, scheme) - if err != nil { - return nil, err - } +type executionError struct { + err error + fatal bool // these errors should not be retried + eventName *string // nil if no warn even should be created +} - ref := corev1.ObjectReference{ - Kind: gvk.Kind, - Name: instance.Name, - Namespace: instance.Namespace, +func (e executionError) Error() string { + if e.fatal { + return fmt.Sprintf("Fatal error: %v", e.err) + } else { + return fmt.Sprintf("Error during execution: %v", e.err) } - planExecution := kudov1alpha1.PlanExecution{ - TypeMeta: metav1.TypeMeta{ - Kind: "PlanExecution", - APIVersion: "kudo.dev/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%v-%v-%v", instance.Name, planName, time.Now().Nanosecond()), - Namespace: instance.GetNamespace(), - // TODO: Should also add one for Operator in here as well. - Labels: map[string]string{ - kudo.OperatorVersionAnnotation: instance.Spec.OperatorVersion.Name, - kudo.InstanceLabel: instance.Name, - }, - }, - Spec: kudov1alpha1.PlanExecutionSpec{ - Instance: ref, - PlanName: planName, - Template: instance.Spec, - }, - } - - return &planExecution, nil } diff --git a/pkg/controller/planexecution/plan_execution_engine.go b/pkg/controller/instance/plan_execution_engine.go similarity index 83% rename from pkg/controller/planexecution/plan_execution_engine.go rename to pkg/controller/instance/plan_execution_engine.go index 3f80cea80..f3d6a5986 100644 --- a/pkg/controller/planexecution/plan_execution_engine.go +++ b/pkg/controller/instance/plan_execution_engine.go @@ -1,7 +1,8 @@ -package planexecution +package instance import ( "context" + "encoding/json" "fmt" "log" "strconv" @@ -20,8 +21,8 @@ import ( ) type activePlan struct { - Name string - State *v1alpha1.PlanExecutionStatus + Name string + *v1alpha1.PlanStatus Spec *v1alpha1.Plan Tasks map[string]v1alpha1.TaskSpec Templates map[string]string @@ -54,19 +55,19 @@ type executionMetadata struct { // result of running this function is new state of the execution that is returned to the caller (it can either be completed, or still in progress or errored) // in case of error, error is returned along with the state as well (so that it's possible to report which step caused the error) // in case of error, method returns both error or fatalError which should indicate unrecoverable error meaning there is no point in retrying that execution -func executePlan(plan *activePlan, metadata *executionMetadata, c client.Client, renderer kubernetesObjectEnhancer) (*v1alpha1.PlanExecutionStatus, error) { - if isFinished(plan.State.State) { - log.Printf("PlanExecution: Plan %s for instance %s is completed, nothing to do", plan.Name, metadata.instanceName) - return plan.State, nil +func executePlan(plan *activePlan, metadata *executionMetadata, c client.Client, renderer kubernetesObjectEnhancer) (*v1alpha1.PlanStatus, error) { + if plan.Status.IsTerminal() { + log.Printf("PlanExecution: Plan %s for instance %s is terminal, nothing to do", plan.Name, metadata.instanceName) + return plan.PlanStatus, nil } // we don't want to modify the original state, and State does not contain any pointer, so shallow copy is enough - newState := &(*plan.State) + newState := &(*plan.PlanStatus) // render kubernetes resources needed to execute this plan planResources, err := prepareKubeResources(plan, metadata, renderer) if err != nil { - newState.State = v1alpha1.PhaseStateError + newState.Status = v1alpha1.ExecutionError return newState, err } @@ -74,12 +75,12 @@ func executePlan(plan *activePlan, metadata *executionMetadata, c client.Client, allPhasesCompleted := true for _, ph := range plan.Spec.Phases { currentPhaseState, _ := getPhaseFromStatus(ph.Name, newState) - if isFinished(currentPhaseState.State) { + if isFinished(currentPhaseState.Status) { // nothing to do - log.Printf("PlanExecution: Phase %s on plan %s and instance %s is in state %s, nothing to do", ph.Name, plan.Name, metadata.instanceName, currentPhaseState.State) + log.Printf("PlanExecution: Phase %s on plan %s and instance %s is in state %s, nothing to do", ph.Name, plan.Name, metadata.instanceName, currentPhaseState.Status) continue - } else if isInProgress(currentPhaseState.State) { - currentPhaseState.State = v1alpha1.PhaseStateInProgress + } else if isInProgress(currentPhaseState.Status) { + currentPhaseState.Status = v1alpha1.ExecutionInProgress log.Printf("PlanExecution: Executing phase %s on plan %s and instance %s - it's in progress", ph.Name, plan.Name, metadata.instanceName) // we're currently executing this phase @@ -89,15 +90,15 @@ func executePlan(plan *activePlan, metadata *executionMetadata, c client.Client, resources := planResources.PhaseResources[ph.Name].StepResources[st.Name] log.Printf("Resources count: %d", len(resources)) - log.Printf("PlanExecution: Executing step %s on plan %s and instance %s - it's in %s state", st.Name, plan.Name, metadata.instanceName, currentStepState.State) + log.Printf("PlanExecution: Executing step %s on plan %s and instance %s - it's in %s state", st.Name, plan.Name, metadata.instanceName, currentStepState.Status) err := executeStep(st, currentStepState, resources, c) if err != nil { - currentPhaseState.State = v1alpha1.PhaseStateError - currentStepState.State = v1alpha1.PhaseStateError + currentPhaseState.Status = v1alpha1.ExecutionError + currentStepState.Status = v1alpha1.ExecutionError return newState, err } - if !isFinished(currentStepState.State) { + if !isFinished(currentStepState.Status) { allStepsHealthy = false if ph.Strategy == v1alpha1.Serial { // we cannot proceed to the next step @@ -108,11 +109,11 @@ func executePlan(plan *activePlan, metadata *executionMetadata, c client.Client, if allStepsHealthy { log.Printf("PlanExecution: All steps on phase %s plan %s and instance %s are healthy", ph.Name, plan.Name, metadata.instanceName) - currentPhaseState.State = v1alpha1.PhaseStateComplete + currentPhaseState.Status = v1alpha1.ExecutionComplete } } - if !isFinished(currentPhaseState.State) { + if !isFinished(currentPhaseState.Status) { // we cannot proceed to the next phase allPhasesCompleted = false break @@ -121,15 +122,15 @@ func executePlan(plan *activePlan, metadata *executionMetadata, c client.Client, if allPhasesCompleted { log.Printf("PlanExecution: All phases on plan %s and instance %s are healthy", plan.Name, metadata.instanceName) - newState.State = v1alpha1.PhaseStateComplete + newState.Status = v1alpha1.ExecutionComplete } return newState, nil } func executeStep(step v1alpha1.Step, state *v1alpha1.StepStatus, resources []runtime.Object, c client.Client) error { - if isInProgress(state.State) { - state.State = v1alpha1.PhaseStateInProgress + if isInProgress(state.Status) { + state.Status = v1alpha1.ExecutionInProgress // check if step is already healthy allHealthy := true @@ -174,12 +175,17 @@ func executeStep(step v1alpha1.Step, state *v1alpha1.StepStatus, resources []run } if allHealthy { - state.State = v1alpha1.PhaseStateComplete + state.Status = v1alpha1.ExecutionComplete } } return nil } +func prettyPrint(i interface{}) string { + s, _ := json.MarshalIndent(i, "", " ") + return string(s) +} + // patchExistingObject calls update method on kubernetes client to make sure the current resource reflects what is on server // // an obvious optimization here would be to not patch when objects are the same, however that is not easy @@ -232,7 +238,7 @@ func prepareKubeResources(plan *activePlan, meta *executionMetadata, renderer ku } for _, phase := range plan.Spec.Phases { - phaseState, _ := getPhaseFromStatus(phase.Name, plan.State) + phaseState, _ := getPhaseFromStatus(phase.Name, plan.PlanStatus) perStepResources := make(map[string][]runtime.Object) result.PhaseResources[phase.Name] = phaseResources{ StepResources: perStepResources, @@ -254,21 +260,21 @@ func prepareKubeResources(plan *activePlan, meta *executionMetadata, renderer ku if resource, ok := plan.Templates[res]; ok { templatedYaml, err := engine.Render(resource, configs) if err != nil { - phaseState.State = v1alpha1.PhaseStateError - stepState.State = v1alpha1.PhaseStateError + phaseState.Status = v1alpha1.ExecutionError + stepState.Status = v1alpha1.ExecutionError err := errors.Wrapf(err, "error expanding template") log.Print(err) - return nil, fatalError{err: err} + return nil, executionError{err, true, nil} } resourcesAsString[res] = templatedYaml } else { - phaseState.State = v1alpha1.PhaseStateError - stepState.State = v1alpha1.PhaseStateError + phaseState.Status = v1alpha1.ExecutionError + stepState.Status = v1alpha1.ExecutionError err := fmt.Errorf("PlanExecution: Error finding resource named %v for operator version %v", res, meta.operatorVersionName) log.Print(err) - return nil, fatalError{err: err} + return nil, executionError{err, true, nil} } } @@ -284,20 +290,20 @@ func prepareKubeResources(plan *activePlan, meta *executionMetadata, renderer ku }, meta.resourcesOwner) if err != nil { - phaseState.State = v1alpha1.PhaseStateError - stepState.State = v1alpha1.PhaseStateError + phaseState.Status = v1alpha1.ExecutionError + stepState.Status = v1alpha1.ExecutionError log.Printf("Error creating Kubernetes objects from step %v in phase %v of plan %v: %v", step.Name, phase.Name, meta.planExecutionID, err) return nil, err } resources = append(resources, resourcesWithConventions...) } else { - phaseState.State = v1alpha1.PhaseStateError - stepState.State = v1alpha1.PhaseStateError + phaseState.Status = v1alpha1.ExecutionError + stepState.Status = v1alpha1.ExecutionError err := fmt.Errorf("Error finding task named %s for operator version %s", taskSpec, meta.operatorVersionName) log.Print(err) - return nil, fatalError{err: err} + return nil, executionError{err, false, nil} } } @@ -317,7 +323,7 @@ func getStepFromStatus(stepName string, status *v1alpha1.PhaseStatus) (*v1alpha1 return nil, fmt.Errorf("PlanExecution: Cannot find step %s in plan", stepName) } -func getPhaseFromStatus(phaseName string, status *v1alpha1.PlanExecutionStatus) (*v1alpha1.PhaseStatus, error) { +func getPhaseFromStatus(phaseName string, status *v1alpha1.PlanStatus) (*v1alpha1.PhaseStatus, error) { for i, p := range status.Phases { if p.Name == phaseName { return &status.Phases[i], nil @@ -326,10 +332,10 @@ func getPhaseFromStatus(phaseName string, status *v1alpha1.PlanExecutionStatus) return nil, fmt.Errorf("PlanExecution: Cannot find phase %s in plan", phaseName) } -func isFinished(state v1alpha1.PhaseState) bool { - return state == v1alpha1.PhaseStateComplete +func isFinished(state v1alpha1.ExecutionStatus) bool { + return state == v1alpha1.ExecutionComplete } -func isInProgress(state v1alpha1.PhaseState) bool { - return state == v1alpha1.PhaseStateInProgress || state == v1alpha1.PhaseStatePending || state == v1alpha1.PhaseStateError +func isInProgress(state v1alpha1.ExecutionStatus) bool { + return state == v1alpha1.ExecutionInProgress || state == v1alpha1.ExecutionPending || state == v1alpha1.ExecutionError } diff --git a/pkg/controller/planexecution/plan_execution_engine_test.go b/pkg/controller/instance/plan_execution_engine_test.go similarity index 67% rename from pkg/controller/planexecution/plan_execution_engine_test.go rename to pkg/controller/instance/plan_execution_engine_test.go index d9e5623e8..fcc1f080c 100644 --- a/pkg/controller/planexecution/plan_execution_engine_test.go +++ b/pkg/controller/instance/plan_execution_engine_test.go @@ -1,4 +1,4 @@ -package planexecution +package instance import ( "reflect" @@ -32,23 +32,22 @@ func TestExecutePlan(t *testing.T) { name string activePlan *activePlan metadata *executionMetadata - expectedStatus *v1alpha1.PlanExecutionStatus + expectedStatus *v1alpha1.PlanStatus }{ {"plan already finished", &activePlan{ Name: "test", - State: &v1alpha1.PlanExecutionStatus{ - State: v1alpha1.PhaseStateComplete, + PlanStatus: &v1alpha1.PlanStatus{ + Status: v1alpha1.ExecutionComplete, }, - }, defaultMetadata, &v1alpha1.PlanExecutionStatus{ - State: v1alpha1.PhaseStateComplete, + }, defaultMetadata, &v1alpha1.PlanStatus{ + Status: v1alpha1.ExecutionComplete, }}, {"plan with one step to be executed, still in progress", &activePlan{ Name: "test", - State: &v1alpha1.PlanExecutionStatus{ - State: v1alpha1.PhaseStatePending, + PlanStatus: &v1alpha1.PlanStatus{ + Status: v1alpha1.ExecutionPending, Name: "test", - Strategy: "serial", - Phases: []v1alpha1.PhaseStatus{{Strategy: "serial", Name: "phase", State: v1alpha1.PhaseStatePending, Steps: []v1alpha1.StepStatus{{State: v1alpha1.PhaseStatePending, Name: "step"}}}}, + Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionPending, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionPending, Name: "step"}}}}, }, Spec: &v1alpha1.Plan{ Strategy: "serial", @@ -58,20 +57,18 @@ func TestExecutePlan(t *testing.T) { }, Tasks: map[string]v1alpha1.TaskSpec{"task": {Resources: []string{"job"}}}, Templates: map[string]string{"job": getResourceAsString(getJob("job1", "default"))}, - }, defaultMetadata, &v1alpha1.PlanExecutionStatus{ - State: v1alpha1.PhaseStatePending, + }, defaultMetadata, &v1alpha1.PlanStatus{ + Status: v1alpha1.ExecutionPending, Name: "test", - Strategy: "serial", - Phases: []v1alpha1.PhaseStatus{{Strategy: "serial", Name: "phase", State: v1alpha1.PhaseStateInProgress, Steps: []v1alpha1.StepStatus{{State: v1alpha1.PhaseStateInProgress, Name: "step"}}}}, + Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionInProgress, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionInProgress, Name: "step"}}}}, }}, // this plan deploys pod, that is marked as healthy immediately because we cannot evaluate health {"plan with one step, immediately healthy -> completed", &activePlan{ Name: "test", - State: &v1alpha1.PlanExecutionStatus{ - State: v1alpha1.PhaseStatePending, + PlanStatus: &v1alpha1.PlanStatus{ + Status: v1alpha1.ExecutionPending, Name: "test", - Strategy: "serial", - Phases: []v1alpha1.PhaseStatus{{Strategy: "serial", Name: "phase", State: v1alpha1.PhaseStatePending, Steps: []v1alpha1.StepStatus{{State: v1alpha1.PhaseStatePending, Name: "step"}}}}, + Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionPending, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionPending, Name: "step"}}}}, }, Spec: &v1alpha1.Plan{ Strategy: "serial", @@ -81,19 +78,17 @@ func TestExecutePlan(t *testing.T) { }, Tasks: map[string]v1alpha1.TaskSpec{"task": {Resources: []string{"pod"}}}, Templates: map[string]string{"pod": getResourceAsString(getPod("pod1", "default"))}, - }, defaultMetadata, &v1alpha1.PlanExecutionStatus{ - State: v1alpha1.PhaseStateComplete, + }, defaultMetadata, &v1alpha1.PlanStatus{ + Status: v1alpha1.ExecutionComplete, Name: "test", - Strategy: "serial", - Phases: []v1alpha1.PhaseStatus{{Strategy: "serial", Name: "phase", State: v1alpha1.PhaseStateComplete, Steps: []v1alpha1.StepStatus{{State: v1alpha1.PhaseStateComplete, Name: "step"}}}}, + Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionComplete, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionComplete, Name: "step"}}}}, }}, {"plan in errored state will be retried and completed when no error happens", &activePlan{ Name: "test", - State: &v1alpha1.PlanExecutionStatus{ - State: v1alpha1.PhaseStateError, + PlanStatus: &v1alpha1.PlanStatus{ + Status: v1alpha1.ExecutionError, Name: "test", - Strategy: "serial", - Phases: []v1alpha1.PhaseStatus{{Strategy: "serial", Name: "phase", State: v1alpha1.PhaseStateError, Steps: []v1alpha1.StepStatus{{State: v1alpha1.PhaseStateError, Name: "step"}}}}, + Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionError, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionError, Name: "step"}}}}, }, Spec: &v1alpha1.Plan{ Strategy: "serial", @@ -103,11 +98,10 @@ func TestExecutePlan(t *testing.T) { }, Tasks: map[string]v1alpha1.TaskSpec{"task": {Resources: []string{"pod"}}}, Templates: map[string]string{"pod": getResourceAsString(getPod("pod1", "default"))}, - }, defaultMetadata, &v1alpha1.PlanExecutionStatus{ - State: v1alpha1.PhaseStateComplete, + }, defaultMetadata, &v1alpha1.PlanStatus{ + Status: v1alpha1.ExecutionComplete, Name: "test", - Strategy: "serial", - Phases: []v1alpha1.PhaseStatus{{Strategy: "serial", Name: "phase", State: v1alpha1.PhaseStateComplete, Steps: []v1alpha1.StepStatus{{State: v1alpha1.PhaseStateComplete, Name: "step"}}}}, + Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionComplete, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionComplete, Name: "step"}}}}, }}, } diff --git a/pkg/controller/instance2/instance_controller.go b/pkg/controller/instance2/instance_controller.go deleted file mode 100644 index 607587620..000000000 --- a/pkg/controller/instance2/instance_controller.go +++ /dev/null @@ -1,87 +0,0 @@ -/* - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package instance - -import ( - "context" - "log" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/client-go/tools/record" - - kudov1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" - appsv1 "k8s.io/api/apps/v1" - batchv1 "k8s.io/api/batch/v1" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -// Reconciler reconciles an Instance object. -type Reconciler struct { - client.Client - recorder record.EventRecorder -} - -// SetupWithManager registers this reconciler with the controller manager -func (r *Reconciler) SetupWithManager( - mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&kudov1alpha1.Instance{}). - Owns(&kudov1alpha1.Instance{}). - Owns(&appsv1.Deployment{}). - Owns(&batchv1.Job{}). - Owns(&appsv1.StatefulSet{}). - Complete(r) -} - -// Reconcile ... -// -// Automatically generate RBAC rules to allow the Controller to read and write Deployments -// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=kudo.dev,resources=instances,verbs=get;list;watch;create;update;patch;delete -func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { - // ---------- 1. Query the current state ---------- - - log.Printf("InstanceController: Received Reconcile request for instance \"%+v\"", request.Name) - instance, err := r.getInstance(request) - if err != nil { - return reconcile.Result{}, err - } - // query OV - - // ---------- 2. If we're currently running plan, continue with the execution ---------- - - // ---------- 3. Update status of instance after the execution proceeded ---------- - - return reconcile.Result{}, nil -} - -// getInstance retrieves the instance by namespaced name -// returns nil, nil when instance is not found (not found is not considered an error) -func (r *Reconciler) getInstance(request ctrl.Request) (instance *kudov1alpha1.Instance, err error) { - instance = &kudov1alpha1.Instance{} - err = r.Get(context.TODO(), request.NamespacedName, instance) - if err != nil { - if errors.IsNotFound(err) { - // Object not found, return. Created objects are automatically garbage collected. - // For additional cleanup logic use finalizers. - return nil, nil - } - // Error reading the object - requeue the request. - return nil, err - } - return instance, nil -} \ No newline at end of file diff --git a/pkg/controller/planexecution/planexecution_controller.go b/pkg/controller/planexecution/planexecution_controller.go deleted file mode 100644 index ba5d470e4..000000000 --- a/pkg/controller/planexecution/planexecution_controller.go +++ /dev/null @@ -1,480 +0,0 @@ -/* - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package planexecution - -import ( - "context" - "encoding/json" - "fmt" - "log" - "strings" - - "github.com/kudobuilder/kudo/pkg/util/kudo" - - kudov1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" - appsv1 "k8s.io/api/apps/v1" - batchv1 "k8s.io/api/batch/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/record" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" -) - -// Add creates a new PlanExecution Controller and adds it to the Manager with default RBAC. -// The Manager will set fields on the Controller and Start it when the Manager is Started. -func Add(mgr manager.Manager) error { - log.Printf("PlanExecutionController: Registering planexecution controller.") - - return add(mgr, newReconciler(mgr)) -} - -// newReconciler returns a new reconcile.Reconciler -func newReconciler(mgr manager.Manager) reconcile.Reconciler { - return &ReconcilePlanExecution{Client: mgr.GetClient(), scheme: mgr.GetScheme(), recorder: mgr.GetEventRecorderFor("planexecution-controller")} -} - -// add adds a new Controller to mgr with r as the reconcile.Reconciler -func add(mgr manager.Manager, r reconcile.Reconciler) error { - // Create a new controller - c, err := controller.New("planexecution-controller", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } - - // Watch for Deployments, Jobs and StatefulSets - // - // Define a mapping from the object in the event to one or more objects to - // Reconcile. Specifically this calls for a reconciliation of any owned - // objects. - mapToOwningInstanceActivePlan := handler.ToRequestsFunc( - func(a handler.MapObject) []reconcile.Request { - owners := a.Meta.GetOwnerReferences() - requests := make([]reconcile.Request, 0) - for _, owner := range owners { - // if owner is an instance, we also want to queue up the PlanExecution - // in the Status section - inst := &kudov1alpha1.Instance{} - err = mgr.GetClient().Get(context.TODO(), client.ObjectKey{ - Name: owner.Name, - Namespace: a.Meta.GetNamespace(), - }, inst) - - if err != nil { - log.Printf("PlanExecutionController: Error getting instance object: %v", err) - } else { - log.Printf("PlanExecutionController: Adding \"%v\" to reconcile", inst.Status.ActivePlan.Name) - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Name: inst.Status.ActivePlan.Name, - Namespace: inst.Status.ActivePlan.Namespace, - }, - }) - } - } - return requests - }) - - // 'UpdateFunc' and 'CreateFunc' are used to judge if a event about the object is what - // we want. If return true, the event will be processed by the reconciler. - // - // PlanExecutions should be mostly immutable. - p := predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - log.Printf("PlanExecutionController: Received update event for an instance named: %v", e.MetaNew.GetName()) - return e.ObjectOld != e.ObjectNew - }, - CreateFunc: func(e event.CreateEvent) bool { - log.Printf("PlanExecutionController: Received create event for an instance named: %v", e.Meta.GetName()) - return true - }, - DeleteFunc: func(e event.DeleteEvent) bool { - // TODO: send event for Instance that plan was deleted - log.Printf("PlanExecutionController: Received delete event for an instance named: %v", e.Meta.GetName()) - return true - }, - } - - // Watch for changes to PlanExecution - err = c.Watch(&source.Kind{Type: &kudov1alpha1.PlanExecution{}}, &handler.EnqueueRequestForObject{}) - if err != nil { - return err - } - - // Watch Deployments and trigger Reconciles for objects mapped from the Deployment in the event - err = c.Watch( - &source.Kind{Type: &appsv1.StatefulSet{}}, - &handler.EnqueueRequestsFromMapFunc{ - ToRequests: mapToOwningInstanceActivePlan, - }, - p) - if err != nil { - return err - } - err = c.Watch( - &source.Kind{Type: &appsv1.Deployment{}}, - &handler.EnqueueRequestsFromMapFunc{ - ToRequests: mapToOwningInstanceActivePlan, - }, - p) - if err != nil { - return err - } - err = c.Watch( - &source.Kind{Type: &batchv1.Job{}}, - &handler.EnqueueRequestsFromMapFunc{ - ToRequests: mapToOwningInstanceActivePlan, - }, - p) - if err != nil { - return err - } - - // for instances we're interested in updates of instances owned by some planexecution (instance was created as part of PE) - // but also root instances of an operator that might have been updated with new activeplan - err = c.Watch( - &source.Kind{Type: &kudov1alpha1.Instance{}}, - &handler.EnqueueRequestsFromMapFunc{ - ToRequests: handler.ToRequestsFunc( - func(a handler.MapObject) []reconcile.Request { - // instance in instance -> reconcile plan for outer instance - requests := mapToOwningInstanceActivePlan(a) - - // instance in instance -> reconcile plan for inner instance - inst := &kudov1alpha1.Instance{} - err = mgr.GetClient().Get(context.TODO(), client.ObjectKey{ - Name: a.Meta.GetName(), - Namespace: a.Meta.GetNamespace(), - }, inst) - - if err == nil { - // for every updated/added instance also trigger reconcile for its active plan - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Name: inst.Status.ActivePlan.Name, - Namespace: inst.Status.ActivePlan.Namespace, - }, - }) - } else { - log.Printf("PlanExecutionController: received event from Instance %s/%s but instance of that name does not exist", a.Meta.GetNamespace(), a.Meta.GetName()) - } - - return requests - }), - }, - p) - if err != nil { - return err - } - - return nil -} - -var _ reconcile.Reconciler = &ReconcilePlanExecution{} - -// ReconcilePlanExecution reconciles a PlanExecution object -type ReconcilePlanExecution struct { - client.Client - scheme *runtime.Scheme - recorder record.EventRecorder -} - -// Reconcile reads that state of the cluster for a PlanExecution object and makes changes based on the state read -// and what is in the PlanExecution.Spec -// -// Automatically generate RBAC rules to allow the Controller to read and write Deployments -// +kubebuilder:rbac:groups=apps,resources=deployments;statefulsets,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=kudo.dev,resources=planexecutions;instances,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups="",resources=events;configmaps,verbs=get;list;watch;create;patch -// +kubebuilder:rbac:groups=policy,resources=poddisruptionbudgets;poddisruptionbudgets.policy,verbs=get;list;watch;create;update;patch;delete -func (r *ReconcilePlanExecution) Reconcile(request reconcile.Request) (reconcile.Result, error) { - // Fetch the PlanExecution instance - planExecution := &kudov1alpha1.PlanExecution{} - err := r.Get(context.TODO(), request.NamespacedName, planExecution) - if err != nil { - if errors.IsNotFound(err) { - log.Printf("PlanExecutionController: Error finding planexecution \"%v\": %v", request.Name, err) - // Object not found, return. Created objects are automatically garbage collected. - // For additional cleanup logic use finalizers. - return reconcile.Result{}, nil - } - // Error reading the object - requeue the request. - return reconcile.Result{}, err - } - - instance := &kudov1alpha1.Instance{} - err = r.Get(context.TODO(), - types.NamespacedName{ - Name: planExecution.Spec.Instance.Name, - Namespace: planExecution.Spec.Instance.Namespace, - }, - instance) - if err != nil { - // Can't find the instance. - r.recorder.Event(planExecution, "Warning", "InvalidInstance", fmt.Sprintf("Could not find required instance (%v)", planExecution.Spec.Instance.Name)) - planExecution.Status.State = kudov1alpha1.PhaseStateError - log.Printf("PlanExecutionController: Error getting Instance %v in %v: %v", - planExecution.Spec.Instance.Name, - planExecution.Spec.Instance.Namespace, - err) - - if errors.IsNotFound(err) { - return reconcile.Result{}, nil - } - - return reconcile.Result{}, err - } - - if instance.Status.ActivePlan.Name != planExecution.Name || instance.Status.ActivePlan.Namespace != planExecution.Namespace { - // this can happen for newly created PlanExecution where ActivePlan was not yet set to point to this instance - // this will get retried thanks to a watch set up for instance updates - log.Printf("instance %s does not have ActivePlan pointing to PlanExecution %s, %s. Instead %s, %s", instance.Name, planExecution.Name, planExecution.Namespace, instance.Status.ActivePlan.Name, instance.Status.ActivePlan.Namespace) - return reconcile.Result{}, nil - } - - // Check for Suspend set. - if planExecution.Spec.Suspend != nil && *planExecution.Spec.Suspend { - planExecution.Status.State = kudov1alpha1.PhaseStateSuspend - err = r.Update(context.TODO(), planExecution) - r.recorder.Event(instance, "Normal", "PlanSuspend", fmt.Sprintf("PlanExecution %v suspended", planExecution.Name)) - return reconcile.Result{}, err - } - - // See if this has already been processed - if planExecution.Status.State == kudov1alpha1.PhaseStateComplete { - log.Printf("PlanExecutionController: PlanExecution \"%v\" has already run to completion, not processing.", planExecution.Name) - return reconcile.Result{}, nil - } - - // Get associated OperatorVersion - operatorVersion := &kudov1alpha1.OperatorVersion{} - err = r.Get(context.TODO(), - types.NamespacedName{ - Name: instance.Spec.OperatorVersion.Name, - Namespace: instance.GetOperatorVersionNamespace(), - }, - operatorVersion) - if err != nil { - // Can't find the OperatorVersion. - planExecution.Status.State = kudov1alpha1.PhaseStateError - r.recorder.Event(planExecution, "Warning", "InvalidOperatorVersion", fmt.Sprintf("Could not find OperatorVersion %v", instance.Spec.OperatorVersion.Name)) - log.Printf("PlanExecutionController: Error getting OperatorVersion %v in %v: %v", - instance.Spec.OperatorVersion.Name, - instance.GetOperatorVersionNamespace(), - err) - return reconcile.Result{}, err - } - - params, err := getParameters(instance, operatorVersion) - if err != nil { - log.Printf("PlanExecutionController: %v", err) - r.recorder.Event(planExecution, "Warning", "MissingParameter", err.Error()) - return reconcile.Result{}, nil // do not retry this error - } - - executedPlan, ok := operatorVersion.Spec.Plans[planExecution.Spec.PlanName] - if !ok { - r.recorder.Event(planExecution, "Warning", "InvalidPlan", fmt.Sprintf("Could not find required plan (%v)", planExecution.Spec.PlanName)) - err = fmt.Errorf("could not find required plan (%v)", planExecution.Spec.PlanName) - planExecution.Status.State = kudov1alpha1.PhaseStateError - return reconcile.Result{}, err - } - - planExecution = planExecution.DeepCopy() - activePlan := &activePlan{ - Name: planExecution.Spec.PlanName, - Spec: &executedPlan, - State: &planExecution.Status, - Tasks: operatorVersion.Spec.Tasks, - Templates: operatorVersion.Spec.Templates, - params: params, - } - initializePlanStatus(&planExecution.Status, activePlan) - - log.Printf("PlanExecutionController: Going to execute plan %s for instance %s", planExecution.Name, instance.Name) - newState, err := executePlan(activePlan, &executionMetadata{ - operatorVersionName: operatorVersion.Name, - operatorVersion: operatorVersion.Spec.Version, - resourcesOwner: instance, - operatorName: operatorVersion.Spec.Operator.Name, - instanceNamespace: instance.Namespace, - instanceName: instance.Name, - planExecutionID: planExecution.Name, - }, r.Client, &kustomizeEnhancer{r.scheme}) - if newState != nil { - planExecution.Status = *newState - } - - if err != nil { - log.Printf("PlanExecutionController: error when executing plan for instance %s: %v", instance.Name, err) - - err = r.Client.Update(context.TODO(), planExecution) - if err != nil { - log.Printf("PlanExecutionController: Error when updating planExecution state. %v", err) - return reconcile.Result{}, err - } - - if _, ok := err.(*fatalError); ok { - // do not retry - return reconcile.Result{}, nil - } - return reconcile.Result{}, err - } - - err = r.Client.Update(context.TODO(), planExecution) - if err != nil { - log.Printf("PlanExecutionController: Error when updating planExecution state. %v", err) - return reconcile.Result{}, err - } - - // update instance state - // TODO this should not be done in this controller, we should address it in another iteration of refactoring - instance.Status.Status = planExecution.Status.State - err = r.Client.Update(context.TODO(), instance) - if err != nil { - log.Printf("Error updating instance status to %v: %v\n", instance.Status.Status, err) - return reconcile.Result{}, err - } - - return reconcile.Result{}, nil -} - -// initializePlanStatus constructs the current plan execution summary by consulting current state of PE CRD and selected plan from OV -func initializePlanStatus(status *kudov1alpha1.PlanExecutionStatus, plan *activePlan) { - if plan.Name == status.Name && status.State != kudov1alpha1.PhaseStateComplete { - // nothing to do, plan is already in progress and was populated in previous iteration - return - } - - status.State = kudov1alpha1.PhaseStateInProgress - status.Name = plan.Name - status.Strategy = plan.Spec.Strategy - status.Phases = make([]kudov1alpha1.PhaseStatus, 0) - - // plan execution might not yet be initialized, make sure we have all phases and steps covered - for _, p := range plan.Spec.Phases { - phaseState := &kudov1alpha1.PhaseStatus{ - Name: p.Name, - State: kudov1alpha1.PhaseStatePending, - Strategy: p.Strategy, - Steps: make([]kudov1alpha1.StepStatus, 0), - } - - for _, s := range p.Steps { - stepState := &kudov1alpha1.StepStatus{ - Name: s.Name, - State: kudov1alpha1.PhaseStatePending, - } - phaseState.Steps = append(phaseState.Steps, *stepState) - } - - status.Phases = append(status.Phases, *phaseState) - } -} - -// fatalError is representing type of error that is non-recoverable (like bug in the template preventing rendering) -// we should not retry these errors -type fatalError struct { - err error -} - -func (e fatalError) Error() string { - return fmt.Sprintf("Fatal error: %v", e.err) -} - -func getParameters(instance *kudov1alpha1.Instance, operatorVersion *kudov1alpha1.OperatorVersion) (map[string]string, error) { - params := make(map[string]string) - - for k, v := range instance.Spec.Parameters { - params[k] = v - } - - missingRequiredParameters := make([]string, 0) - // Merge defaults with customizations - for _, param := range operatorVersion.Spec.Parameters { - _, ok := params[param.Name] - if !ok && param.Required && param.Default == nil { - // instance does not define this parameter and there is no default while the parameter is required -> error - missingRequiredParameters = append(missingRequiredParameters, param.Name) - - } else if !ok { - params[param.Name] = kudo.StringValue(param.Default) - } - } - - if len(missingRequiredParameters) != 0 { - return nil, fmt.Errorf("parameters are missing when evaluating template: %s", strings.Join(missingRequiredParameters, ",")) - } - - return params, nil -} - -// Cleanup modifies objects on the cluster to allow for the provided obj to get CreateOrApply. -// Currently only needs to clean up Jobs that get run from multiplePlanExecutions -func (r *ReconcilePlanExecution) Cleanup(obj runtime.Object) error { - - switch obj := obj.(type) { - case *batchv1.Job: - // We need to see if there's a current job on the system that matches this exactly (with labels) - log.Printf("PlanExecutionController.Cleanup: *batchv1.Job %v", obj.Name) - - present := &batchv1.Job{} - key, _ := client.ObjectKeyFromObject(obj) - err := r.Get(context.TODO(), key, present) - if errors.IsNotFound(err) { - // This is fine, its good to go - log.Printf("PlanExecutionController: Could not find job \"%v\" in cluster. Good to make a new one.", key) - return nil - } - if err != nil { - // Something else happened - return err - } - // See if the job in the cluster has the same labels as the one we're looking to add. - for k, v := range obj.Labels { - if v != present.Labels[k] { - // Need to delete the present job since its got labels that aren't the same - log.Printf("PlanExecutionController: Different values for job key \"%v\": \"%v\" and \"%v\"", k, v, present.Labels[k]) - err = r.Delete(context.TODO(), present) - return err - } - } - for k, v := range present.Labels { - if v != obj.Labels[k] { - // Need to delete the present job since its got labels that aren't the same - log.Printf("PlanExecutionController: Different values for job key \"%v\": \"%v\" and \"%v\"", k, v, obj.Labels[k]) - err = r.Delete(context.TODO(), present) - return err - } - } - return nil - } - - return nil -} - -func prettyPrint(i interface{}) string { - s, _ := json.MarshalIndent(i, "", " ") - return string(s) -} diff --git a/pkg/util/health/ready.go b/pkg/util/health/ready.go index b7e79e821..78d73afcd 100644 --- a/pkg/util/health/ready.go +++ b/pkg/util/health/ready.go @@ -1,7 +1,6 @@ package health import ( - "context" "fmt" "log" @@ -42,25 +41,12 @@ func IsHealthy(c client.Client, obj runtime.Object) error { } return fmt.Errorf("job \"%v\" still running or failed", obj.Name) case *kudov1alpha1.Instance: - // Instances are healthy when their Active Plan has succeeded - plan := &kudov1alpha1.PlanExecution{} - if obj.Status.ActivePlan.Name == "" { - return fmt.Errorf("checking health of instance %s: not healthy because does not have any active plan assigned yet", obj.Name) - } - err := c.Get(context.TODO(), client.ObjectKey{ - Name: obj.Status.ActivePlan.Name, - Namespace: obj.Status.ActivePlan.Namespace, - }, plan) - if err != nil { - log.Printf("Error getting PlaneExecution %v/%v: %v\n", obj.Status.ActivePlan.Name, obj.Status.ActivePlan.Namespace, err) - return fmt.Errorf("instance active plan not found: %v", err) - } - log.Printf("HealthUtil: Instance %v is in state %v", obj.Name, plan.Status.State) + log.Printf("HealthUtil: Instance %v is in state %v", obj.Name, obj.Status.AggregatedStatus.Status) - if plan.Status.State == kudov1alpha1.PhaseStateComplete { + if obj.Status.AggregatedStatus.Status == kudov1alpha1.ExecutionComplete { return nil } - return fmt.Errorf("instance's active plan is in state %v", plan.Status.State) + return fmt.Errorf("instance's active plan is in state %v", obj.Status.AggregatedStatus.Status) // unless we build logic for what a healthy object is, assume it's healthy when created. default: From 673bc9bd49b7b3e9e68ade2ddd1e98b0ecdbc6a4 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Fri, 20 Sep 2019 21:45:29 +0200 Subject: [PATCH 07/29] Typo --- pkg/controller/instance/instance_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/instance/instance_controller.go b/pkg/controller/instance/instance_controller.go index 7974c37b7..e485e020d 100644 --- a/pkg/controller/instance/instance_controller.go +++ b/pkg/controller/instance/instance_controller.go @@ -84,7 +84,7 @@ func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { // ---------- 3. If we're currently running plan, continue with the execution ---------- activePlanStatus := instance.GetPlanInProgress() - if activePlanStatus == nil { // we have plan in progress + if activePlanStatus == nil { // we have no plan in progress return reconcile.Result{}, nil } From 4dcfc66cb7467f7765546b99fd148d738ec0eeae Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Fri, 20 Sep 2019 22:05:35 +0200 Subject: [PATCH 08/29] Remove dead code, better registration --- cmd/manager/main.go | 14 +- .../kudo/v1alpha1/fake/fake_planexecution.go | 138 ------------- .../typed/kudo/v1alpha1/planexecution.go | 189 ------------------ .../kudo/v1alpha1/planexecution.go | 87 -------- .../listers/kudo/v1alpha1/planexecution.go | 92 --------- pkg/controller/controller.go | 38 ---- .../instance/instance_controller.go | 12 +- pkg/test/harness.go | 35 +++- 8 files changed, 47 insertions(+), 558 deletions(-) delete mode 100644 pkg/client/clientset/versioned/typed/kudo/v1alpha1/fake/fake_planexecution.go delete mode 100644 pkg/client/clientset/versioned/typed/kudo/v1alpha1/planexecution.go delete mode 100644 pkg/client/informers/externalversions/kudo/v1alpha1/planexecution.go delete mode 100644 pkg/client/listers/kudo/v1alpha1/planexecution.go delete mode 100644 pkg/controller/controller.go diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 5f6abac77..cce61863b 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -17,6 +17,7 @@ package main import ( "fmt" + "github.com/kudobuilder/kudo/pkg/controller/instance" "os" "github.com/kudobuilder/kudo/pkg/controller/operatorversion" @@ -28,7 +29,6 @@ import ( "github.com/kudobuilder/kudo/pkg/version" kudov1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" - "github.com/kudobuilder/kudo/pkg/controller" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" @@ -87,10 +87,14 @@ func main() { os.Exit(1) } - log.Info("Setting up controllers") - // TODO this still registers instance and PE controller, this will be refactored in the next phase - if err := controller.AddControllersToManager(mgr); err != nil { - log.Error(err, "unable to register controllers to the manager") + log.Info("Setting up instance controller") + err = (&instance.Reconciler{ + Client: mgr.GetClient(), + Recorder: mgr.GetEventRecorderFor("instance-controller"), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr) + if err != nil { + log.Error(err, "unable to register instance controller to the manager") os.Exit(1) } diff --git a/pkg/client/clientset/versioned/typed/kudo/v1alpha1/fake/fake_planexecution.go b/pkg/client/clientset/versioned/typed/kudo/v1alpha1/fake/fake_planexecution.go deleted file mode 100644 index 2d7d1aedd..000000000 --- a/pkg/client/clientset/versioned/typed/kudo/v1alpha1/fake/fake_planexecution.go +++ /dev/null @@ -1,138 +0,0 @@ -/* - -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. -*/ -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakePlanExecutions implements PlanExecutionInterface -type FakePlanExecutions struct { - Fake *FakeKudoV1alpha1 - ns string -} - -var planexecutionsResource = schema.GroupVersionResource{Group: "kudo.dev", Version: "v1alpha1", Resource: "planexecutions"} - -var planexecutionsKind = schema.GroupVersionKind{Group: "kudo.dev", Version: "v1alpha1", Kind: "PlanExecution"} - -// Get takes name of the planExecution, and returns the corresponding planExecution object, and an error if there is any. -func (c *FakePlanExecutions) Get(name string, options v1.GetOptions) (result *v1alpha1.PlanExecution, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(planexecutionsResource, c.ns, name), &v1alpha1.PlanExecution{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PlanExecution), err -} - -// List takes label and field selectors, and returns the list of PlanExecutions that match those selectors. -func (c *FakePlanExecutions) List(opts v1.ListOptions) (result *v1alpha1.PlanExecutionList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(planexecutionsResource, planexecutionsKind, c.ns, opts), &v1alpha1.PlanExecutionList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.PlanExecutionList{ListMeta: obj.(*v1alpha1.PlanExecutionList).ListMeta} - for _, item := range obj.(*v1alpha1.PlanExecutionList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested planExecutions. -func (c *FakePlanExecutions) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(planexecutionsResource, c.ns, opts)) - -} - -// Create takes the representation of a planExecution and creates it. Returns the server's representation of the planExecution, and an error, if there is any. -func (c *FakePlanExecutions) Create(planExecution *v1alpha1.PlanExecution) (result *v1alpha1.PlanExecution, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(planexecutionsResource, c.ns, planExecution), &v1alpha1.PlanExecution{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PlanExecution), err -} - -// Update takes the representation of a planExecution and updates it. Returns the server's representation of the planExecution, and an error, if there is any. -func (c *FakePlanExecutions) Update(planExecution *v1alpha1.PlanExecution) (result *v1alpha1.PlanExecution, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(planexecutionsResource, c.ns, planExecution), &v1alpha1.PlanExecution{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PlanExecution), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakePlanExecutions) UpdateStatus(planExecution *v1alpha1.PlanExecution) (*v1alpha1.PlanExecution, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(planexecutionsResource, "status", c.ns, planExecution), &v1alpha1.PlanExecution{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PlanExecution), err -} - -// Delete takes name of the planExecution and deletes it. Returns an error if one occurs. -func (c *FakePlanExecutions) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(planexecutionsResource, c.ns, name), &v1alpha1.PlanExecution{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakePlanExecutions) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(planexecutionsResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.PlanExecutionList{}) - return err -} - -// Patch applies the patch and returns the patched planExecution. -func (c *FakePlanExecutions) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.PlanExecution, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(planexecutionsResource, c.ns, name, pt, data, subresources...), &v1alpha1.PlanExecution{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PlanExecution), err -} diff --git a/pkg/client/clientset/versioned/typed/kudo/v1alpha1/planexecution.go b/pkg/client/clientset/versioned/typed/kudo/v1alpha1/planexecution.go deleted file mode 100644 index 1d1e1dfa0..000000000 --- a/pkg/client/clientset/versioned/typed/kudo/v1alpha1/planexecution.go +++ /dev/null @@ -1,189 +0,0 @@ -/* - -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. -*/ -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "time" - - v1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" - scheme "github.com/kudobuilder/kudo/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// PlanExecutionsGetter has a method to return a PlanExecutionInterface. -// A group's client should implement this interface. -type PlanExecutionsGetter interface { - PlanExecutions(namespace string) PlanExecutionInterface -} - -// PlanExecutionInterface has methods to work with PlanExecution resources. -type PlanExecutionInterface interface { - Create(*v1alpha1.PlanExecution) (*v1alpha1.PlanExecution, error) - Update(*v1alpha1.PlanExecution) (*v1alpha1.PlanExecution, error) - UpdateStatus(*v1alpha1.PlanExecution) (*v1alpha1.PlanExecution, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.PlanExecution, error) - List(opts v1.ListOptions) (*v1alpha1.PlanExecutionList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.PlanExecution, err error) - PlanExecutionExpansion -} - -// planExecutions implements PlanExecutionInterface -type planExecutions struct { - client rest.Interface - ns string -} - -// newPlanExecutions returns a PlanExecutions -func newPlanExecutions(c *KudoV1alpha1Client, namespace string) *planExecutions { - return &planExecutions{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the planExecution, and returns the corresponding planExecution object, and an error if there is any. -func (c *planExecutions) Get(name string, options v1.GetOptions) (result *v1alpha1.PlanExecution, err error) { - result = &v1alpha1.PlanExecution{} - err = c.client.Get(). - Namespace(c.ns). - Resource("planexecutions"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of PlanExecutions that match those selectors. -func (c *planExecutions) List(opts v1.ListOptions) (result *v1alpha1.PlanExecutionList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.PlanExecutionList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("planexecutions"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested planExecutions. -func (c *planExecutions) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("planexecutions"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a planExecution and creates it. Returns the server's representation of the planExecution, and an error, if there is any. -func (c *planExecutions) Create(planExecution *v1alpha1.PlanExecution) (result *v1alpha1.PlanExecution, err error) { - result = &v1alpha1.PlanExecution{} - err = c.client.Post(). - Namespace(c.ns). - Resource("planexecutions"). - Body(planExecution). - Do(). - Into(result) - return -} - -// Update takes the representation of a planExecution and updates it. Returns the server's representation of the planExecution, and an error, if there is any. -func (c *planExecutions) Update(planExecution *v1alpha1.PlanExecution) (result *v1alpha1.PlanExecution, err error) { - result = &v1alpha1.PlanExecution{} - err = c.client.Put(). - Namespace(c.ns). - Resource("planexecutions"). - Name(planExecution.Name). - Body(planExecution). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *planExecutions) UpdateStatus(planExecution *v1alpha1.PlanExecution) (result *v1alpha1.PlanExecution, err error) { - result = &v1alpha1.PlanExecution{} - err = c.client.Put(). - Namespace(c.ns). - Resource("planexecutions"). - Name(planExecution.Name). - SubResource("status"). - Body(planExecution). - Do(). - Into(result) - return -} - -// Delete takes name of the planExecution and deletes it. Returns an error if one occurs. -func (c *planExecutions) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("planexecutions"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *planExecutions) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("planexecutions"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched planExecution. -func (c *planExecutions) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.PlanExecution, err error) { - result = &v1alpha1.PlanExecution{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("planexecutions"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/informers/externalversions/kudo/v1alpha1/planexecution.go b/pkg/client/informers/externalversions/kudo/v1alpha1/planexecution.go deleted file mode 100644 index cf9f8df73..000000000 --- a/pkg/client/informers/externalversions/kudo/v1alpha1/planexecution.go +++ /dev/null @@ -1,87 +0,0 @@ -/* - -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. -*/ -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - kudov1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" - versioned "github.com/kudobuilder/kudo/pkg/client/clientset/versioned" - internalinterfaces "github.com/kudobuilder/kudo/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/kudobuilder/kudo/pkg/client/listers/kudo/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// PlanExecutionInformer provides access to a shared informer and lister for -// PlanExecutions. -type PlanExecutionInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.PlanExecutionLister -} - -type planExecutionInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewPlanExecutionInformer constructs a new informer for PlanExecution type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewPlanExecutionInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredPlanExecutionInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredPlanExecutionInformer constructs a new informer for PlanExecution type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredPlanExecutionInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.KudoV1alpha1().PlanExecutions(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.KudoV1alpha1().PlanExecutions(namespace).Watch(options) - }, - }, - &kudov1alpha1.PlanExecution{}, - resyncPeriod, - indexers, - ) -} - -func (f *planExecutionInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredPlanExecutionInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *planExecutionInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&kudov1alpha1.PlanExecution{}, f.defaultInformer) -} - -func (f *planExecutionInformer) Lister() v1alpha1.PlanExecutionLister { - return v1alpha1.NewPlanExecutionLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/listers/kudo/v1alpha1/planexecution.go b/pkg/client/listers/kudo/v1alpha1/planexecution.go deleted file mode 100644 index c1b7f467e..000000000 --- a/pkg/client/listers/kudo/v1alpha1/planexecution.go +++ /dev/null @@ -1,92 +0,0 @@ -/* - -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. -*/ -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// PlanExecutionLister helps list PlanExecutions. -type PlanExecutionLister interface { - // List lists all PlanExecutions in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.PlanExecution, err error) - // PlanExecutions returns an object that can list and get PlanExecutions. - PlanExecutions(namespace string) PlanExecutionNamespaceLister - PlanExecutionListerExpansion -} - -// planExecutionLister implements the PlanExecutionLister interface. -type planExecutionLister struct { - indexer cache.Indexer -} - -// NewPlanExecutionLister returns a new PlanExecutionLister. -func NewPlanExecutionLister(indexer cache.Indexer) PlanExecutionLister { - return &planExecutionLister{indexer: indexer} -} - -// List lists all PlanExecutions in the indexer. -func (s *planExecutionLister) List(selector labels.Selector) (ret []*v1alpha1.PlanExecution, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.PlanExecution)) - }) - return ret, err -} - -// PlanExecutions returns an object that can list and get PlanExecutions. -func (s *planExecutionLister) PlanExecutions(namespace string) PlanExecutionNamespaceLister { - return planExecutionNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// PlanExecutionNamespaceLister helps list and get PlanExecutions. -type PlanExecutionNamespaceLister interface { - // List lists all PlanExecutions in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha1.PlanExecution, err error) - // Get retrieves the PlanExecution from the indexer for a given namespace and name. - Get(name string) (*v1alpha1.PlanExecution, error) - PlanExecutionNamespaceListerExpansion -} - -// planExecutionNamespaceLister implements the PlanExecutionNamespaceLister -// interface. -type planExecutionNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all PlanExecutions in the indexer for a given namespace. -func (s planExecutionNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.PlanExecution, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.PlanExecution)) - }) - return ret, err -} - -// Get retrieves the PlanExecution from the indexer for a given namespace and name. -func (s planExecutionNamespaceLister) Get(name string) (*v1alpha1.PlanExecution, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("planexecution"), name) - } - return obj.(*v1alpha1.PlanExecution), nil -} diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go deleted file mode 100644 index 713b1222a..000000000 --- a/pkg/controller/controller.go +++ /dev/null @@ -1,38 +0,0 @@ -/* - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package controller - -import ( - "github.com/kudobuilder/kudo/pkg/controller/instance" - "github.com/kudobuilder/kudo/pkg/controller/planexecution" - "sigs.k8s.io/controller-runtime/pkg/manager" -) - -// AddControllerToManagerFuncs is a list of functions to add all Controllers to the Manager -var AddControllerToManagerFuncs = []func(manager.Manager) error{ - planexecution.Add, - instance.Add, -} - -// AddControllersToManager adds all Controllers to the Manager -func AddControllersToManager(m manager.Manager) error { - for _, f := range AddControllerToManagerFuncs { - if err := f(m); err != nil { - return err - } - } - return nil -} diff --git a/pkg/controller/instance/instance_controller.go b/pkg/controller/instance/instance_controller.go index e485e020d..f8cefda4a 100644 --- a/pkg/controller/instance/instance_controller.go +++ b/pkg/controller/instance/instance_controller.go @@ -39,8 +39,8 @@ import ( // Reconciler reconciles an Instance object. type Reconciler struct { client.Client - recorder record.EventRecorder - scheme *runtime.Scheme + Recorder record.EventRecorder + Scheme *runtime.Scheme } // SetupWithManager registers this reconciler with the controller manager @@ -93,7 +93,7 @@ func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { err = r.handleError(err, instance) return reconcile.Result{}, err } - newStatus, err := executePlan(activePlan, metadata, r.Client, &kustomizeEnhancer{r.scheme}) + newStatus, err := executePlan(activePlan, metadata, r.Client, &kustomizeEnhancer{r.Scheme}) // ---------- 4. Update status of instance after the execution proceeded ---------- @@ -150,7 +150,7 @@ func (r *Reconciler) handleError(err error, instance *kudov1alpha1.Instance) err log.Printf("InstanceController: %v", err) if exErr, ok := err.(*executionError); ok { if exErr.eventName != nil { - r.recorder.Event(instance, "Warning", kudo.StringValue(exErr.eventName), err.Error()) + r.Recorder.Event(instance, "Warning", kudo.StringValue(exErr.eventName), err.Error()) } if exErr.fatal { @@ -170,7 +170,7 @@ func (r *Reconciler) getInstance(request ctrl.Request) (instance *kudov1alpha1.I log.Printf("InstanceController: Error getting instance \"%v\": %v", instance.Name, err) - r.recorder.Event(instance, "Warning", "InvalidInstance", fmt.Sprintf("Error getting instance \"%v\": %v", instance.Name, err)) + r.Recorder.Event(instance, "Warning", "InvalidInstance", fmt.Sprintf("Error getting instance \"%v\": %v", instance.Name, err)) return nil, err } return instance, nil @@ -191,7 +191,7 @@ func (r *Reconciler) getOperatorVersion(instance *kudov1alpha1.Instance) (ov *ku instance.Spec.OperatorVersion.Name, instance.Name, err) - r.recorder.Event(instance, "Warning", "InvalidOperatorVersion", fmt.Sprintf("Error getting operatorversion \"%v\": %v", ov.Name, err)) + r.Recorder.Event(instance, "Warning", "InvalidOperatorVersion", fmt.Sprintf("Error getting operatorversion \"%v\": %v", ov.Name, err)) return nil, err } return ov, nil diff --git a/pkg/test/harness.go b/pkg/test/harness.go index dd2e4b1e7..d22b956e3 100644 --- a/pkg/test/harness.go +++ b/pkg/test/harness.go @@ -3,6 +3,9 @@ package test import ( "context" "fmt" + "github.com/kudobuilder/kudo/pkg/controller/instance" + "github.com/kudobuilder/kudo/pkg/controller/operator" + "github.com/kudobuilder/kudo/pkg/controller/operatorversion" "io/ioutil" "math/rand" "os" @@ -14,7 +17,6 @@ import ( volumetypes "github.com/docker/docker/api/types/volume" docker "github.com/docker/docker/client" kudo "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" - "github.com/kudobuilder/kudo/pkg/controller" testutils "github.com/kudobuilder/kudo/pkg/test/utils" "k8s.io/client-go/discovery" "k8s.io/client-go/rest" @@ -253,8 +255,35 @@ func (h *Harness) RunKUDO() error { return err } - if err = controller.AddControllersToManager(mgr); err != nil { - return err + // Setup all Controllers + + h.logger.Log("Setting up operator controller") + err = (&operator.Reconciler{ + Client: mgr.GetClient(), + }).SetupWithManager(mgr) + if err != nil { + h.logger.Log(err, "unable to register operator controller to the manager") + os.Exit(1) + } + + h.logger.Log("Setting up operator version controller") + err = (&operatorversion.Reconciler{ + Client: mgr.GetClient(), + }).SetupWithManager(mgr) + if err != nil { + h.logger.Log(err, "unable to register operator controller to the manager") + os.Exit(1) + } + + h.logger.Log("Setting up instance controller") + err = (&instance.Reconciler{ + Client: mgr.GetClient(), + Recorder: mgr.GetEventRecorderFor("instance-controller"), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr) + if err != nil { + h.logger.Log(err, "unable to register instance controller to the manager") + os.Exit(1) } h.managerStopCh = make(chan struct{}) From a927c61693ed360f8cecf3a91d7237daff6449eb Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Sun, 22 Sep 2019 09:37:58 +0200 Subject: [PATCH 09/29] Fix CLI commands and make linter happy --- cmd/manager/main.go | 7 ++-- pkg/apis/kudo/v1alpha1/instance_types.go | 5 ++- .../instance/instance_controller.go | 5 ++- .../instance/plan_execution_engine_test.go | 36 +++++++++---------- pkg/kudoctl/cmd/plan/plan_history.go | 32 +++++++++-------- pkg/kudoctl/cmd/plan/plan_status.go | 36 +++++-------------- pkg/kudoctl/util/kudo/kudo.go | 4 --- pkg/test/harness.go | 11 +++--- 8 files changed, 59 insertions(+), 77 deletions(-) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index cce61863b..6220274db 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -17,9 +17,10 @@ package main import ( "fmt" - "github.com/kudobuilder/kudo/pkg/controller/instance" "os" + "github.com/kudobuilder/kudo/pkg/controller/instance" + "github.com/kudobuilder/kudo/pkg/controller/operatorversion" "github.com/kudobuilder/kudo/pkg/controller/operator" @@ -89,9 +90,9 @@ func main() { log.Info("Setting up instance controller") err = (&instance.Reconciler{ - Client: mgr.GetClient(), + Client: mgr.GetClient(), Recorder: mgr.GetEventRecorderFor("instance-controller"), - Scheme: mgr.GetScheme(), + Scheme: mgr.GetScheme(), }).SetupWithManager(mgr) if err != nil { log.Error(err, "unable to register instance controller to the manager") diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index f451456c9..98c73a352 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -76,17 +76,20 @@ const ExecutionComplete ExecutionStatus = "COMPLETE" // ExecutionError there was an error deploying the application. const ExecutionError ExecutionStatus = "ERROR" -// ExecutionError there was an error deploying the application. +// ExecutionFatalError there was an error deploying the application. const ExecutionFatalError ExecutionStatus = "FATAL_ERROR" +// IsTerminal returns true if the status is terminal (either complete, or in a nonrecoverable error) func (s ExecutionStatus) IsTerminal() bool { return s == ExecutionComplete || s == ExecutionFatalError } +// IsRunning returns true if the plan is currently being executed func (s ExecutionStatus) IsRunning() bool { return s == ExecutionInProgress || s == ExecutionPending || s == ExecutionError } +// GetPlanInProgress returns plan status of currently active plan or nil if no plan is running func (i *Instance) GetPlanInProgress() *PlanStatus { for _, p := range i.Status.PlanStatus { if p.Status.IsRunning() { diff --git a/pkg/controller/instance/instance_controller.go b/pkg/controller/instance/instance_controller.go index f8cefda4a..2948d89a6 100644 --- a/pkg/controller/instance/instance_controller.go +++ b/pkg/controller/instance/instance_controller.go @@ -122,7 +122,7 @@ func preparePlanExecution(instance *kudov1alpha1.Instance, ov *kudov1alpha1.Oper planSpec, ok := ov.Spec.Plans[activePlanStatus.Name] if !ok { - return nil, nil, executionError{fmt.Errorf("Could not find required plan (%v)", activePlanStatus.Name), false, kudo.String("InvalidPlan")} + return nil, nil, executionError{fmt.Errorf("could not find required plan (%v)", activePlanStatus.Name), false, kudo.String("InvalidPlan")} } return &activePlan{ @@ -253,7 +253,6 @@ type executionError struct { func (e executionError) Error() string { if e.fatal { return fmt.Sprintf("Fatal error: %v", e.err) - } else { - return fmt.Sprintf("Error during execution: %v", e.err) } + return fmt.Sprintf("Error during execution: %v", e.err) } diff --git a/pkg/controller/instance/plan_execution_engine_test.go b/pkg/controller/instance/plan_execution_engine_test.go index fcc1f080c..f614f4dae 100644 --- a/pkg/controller/instance/plan_execution_engine_test.go +++ b/pkg/controller/instance/plan_execution_engine_test.go @@ -45,9 +45,9 @@ func TestExecutePlan(t *testing.T) { {"plan with one step to be executed, still in progress", &activePlan{ Name: "test", PlanStatus: &v1alpha1.PlanStatus{ - Status: v1alpha1.ExecutionPending, - Name: "test", - Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionPending, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionPending, Name: "step"}}}}, + Status: v1alpha1.ExecutionPending, + Name: "test", + Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionPending, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionPending, Name: "step"}}}}, }, Spec: &v1alpha1.Plan{ Strategy: "serial", @@ -58,17 +58,17 @@ func TestExecutePlan(t *testing.T) { Tasks: map[string]v1alpha1.TaskSpec{"task": {Resources: []string{"job"}}}, Templates: map[string]string{"job": getResourceAsString(getJob("job1", "default"))}, }, defaultMetadata, &v1alpha1.PlanStatus{ - Status: v1alpha1.ExecutionPending, - Name: "test", - Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionInProgress, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionInProgress, Name: "step"}}}}, + Status: v1alpha1.ExecutionPending, + Name: "test", + Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionInProgress, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionInProgress, Name: "step"}}}}, }}, // this plan deploys pod, that is marked as healthy immediately because we cannot evaluate health {"plan with one step, immediately healthy -> completed", &activePlan{ Name: "test", PlanStatus: &v1alpha1.PlanStatus{ - Status: v1alpha1.ExecutionPending, - Name: "test", - Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionPending, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionPending, Name: "step"}}}}, + Status: v1alpha1.ExecutionPending, + Name: "test", + Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionPending, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionPending, Name: "step"}}}}, }, Spec: &v1alpha1.Plan{ Strategy: "serial", @@ -79,16 +79,16 @@ func TestExecutePlan(t *testing.T) { Tasks: map[string]v1alpha1.TaskSpec{"task": {Resources: []string{"pod"}}}, Templates: map[string]string{"pod": getResourceAsString(getPod("pod1", "default"))}, }, defaultMetadata, &v1alpha1.PlanStatus{ - Status: v1alpha1.ExecutionComplete, - Name: "test", - Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionComplete, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionComplete, Name: "step"}}}}, + Status: v1alpha1.ExecutionComplete, + Name: "test", + Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionComplete, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionComplete, Name: "step"}}}}, }}, {"plan in errored state will be retried and completed when no error happens", &activePlan{ Name: "test", PlanStatus: &v1alpha1.PlanStatus{ - Status: v1alpha1.ExecutionError, - Name: "test", - Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionError, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionError, Name: "step"}}}}, + Status: v1alpha1.ExecutionError, + Name: "test", + Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionError, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionError, Name: "step"}}}}, }, Spec: &v1alpha1.Plan{ Strategy: "serial", @@ -99,9 +99,9 @@ func TestExecutePlan(t *testing.T) { Tasks: map[string]v1alpha1.TaskSpec{"task": {Resources: []string{"pod"}}}, Templates: map[string]string{"pod": getResourceAsString(getPod("pod1", "default"))}, }, defaultMetadata, &v1alpha1.PlanStatus{ - Status: v1alpha1.ExecutionComplete, - Name: "test", - Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionComplete, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionComplete, Name: "step"}}}}, + Status: v1alpha1.ExecutionComplete, + Name: "test", + Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionComplete, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionComplete, Name: "step"}}}}, }}, } diff --git a/pkg/kudoctl/cmd/plan/plan_history.go b/pkg/kudoctl/cmd/plan/plan_history.go index 29f3980f2..2a26ae98f 100644 --- a/pkg/kudoctl/cmd/plan/plan_history.go +++ b/pkg/kudoctl/cmd/plan/plan_history.go @@ -3,7 +3,6 @@ package plan import ( "encoding/json" "fmt" - "time" kudov1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" "github.com/kudobuilder/kudo/pkg/kudoctl/env" @@ -62,10 +61,10 @@ func planHistory(args []string, options *Options, settings *env.Settings) error var labelSelector string if len(args) == 0 { - fmt.Printf("History of all plan-executions for instance \"%s\" in namespace \"%s\":\n", options.Instance, options.Namespace) + fmt.Printf("History of all plan executions for instance \"%s\" in namespace \"%s\":\n", options.Instance, options.Namespace) labelSelector = fmt.Sprintf("instance=%s", options.Instance) } else { - fmt.Printf("History of plan-executions for instance \"%s\" in namespace \"%s\" to operator-version \"%s\":\n", options.Instance, options.Namespace, args[0]) + fmt.Printf("History of plan-executions for instance \"%s\" in namespace \"%s\" for operator-version \"%s\":\n", options.Instance, options.Namespace, args[0]) labelSelector = fmt.Sprintf("operator-version=%s, instance=%s", args[0], options.Instance) } @@ -81,26 +80,29 @@ func planHistory(args []string, options *Options, settings *env.Settings) error return err } - planExecutionList := kudov1alpha1.PlanExecutionList{} + instance := kudov1alpha1.Instance{} - err = json.Unmarshal(mInstObj, &planExecutionList) + err = json.Unmarshal(mInstObj, &instance) if err != nil { return err } tree := treeprint.New() - - if len(planExecutionList.Items) == 0 { - fmt.Printf("No history found for \"%s\" in namespace \"%s\".\n", options.Instance, options.Namespace) - } else { - for _, i := range planExecutionList.Items { - duration := time.Since(i.CreationTimestamp.Time) - historyDisplay := fmt.Sprintf("%s (created %v ago)", i.Name, duration.Round(time.Second)) - tree.AddBranch(historyDisplay) + timeLayout := "2006-01-02" + + for n, p := range instance.Status.PlanStatus { + msg := "never run" + if p.Status != "" && !p.LastFinishedRun.IsZero() { // plan already finished + t := p.LastFinishedRun.Format(timeLayout) + msg = fmt.Sprintf("last run at %s", t) + } else if p.Status.IsRunning() { + msg = "is running" } - - fmt.Println(tree.String()) + historyDisplay := fmt.Sprintf("%s (%s)", n, msg) + tree.AddBranch(historyDisplay) } + fmt.Println(tree.String()) + return nil } diff --git a/pkg/kudoctl/cmd/plan/plan_status.go b/pkg/kudoctl/cmd/plan/plan_status.go index 8ca18ae44..ddb429607 100644 --- a/pkg/kudoctl/cmd/plan/plan_status.go +++ b/pkg/kudoctl/cmd/plan/plan_status.go @@ -97,45 +97,25 @@ func planStatus(options *Options, settings *env.Settings) error { return err } - planExecutionsGVR := schema.GroupVersionResource{ - Group: "kudo.dev", - Version: "v1alpha1", - Resource: "planexecutions", - } + activePlanStatus := instance.GetPlanInProgress() - if instance.Status.ActivePlan.Name == "" { + if activePlanStatus == nil { log.Printf("No active plan exists for instance %s", instance.Name) return nil } - activePlanObj, err := dynamicClient.Resource(planExecutionsGVR).Namespace(options.Namespace).Get(instance.Status.ActivePlan.Name, metav1.GetOptions{}) - if err != nil { - return err - } - - mPlanObj, err := activePlanObj.MarshalJSON() - if err != nil { - return err - } - - activePlanType := kudov1alpha1.PlanExecution{} - - err = json.Unmarshal(mPlanObj, &activePlanType) - if err != nil { - return err - } - rootDisplay := fmt.Sprintf("%s (Operator-Version: \"%s\" Active-Plan: \"%s\")", instance.Name, instance.Spec.OperatorVersion.Name, instance.Status.ActivePlan.Name) + rootDisplay := fmt.Sprintf("%s (Operator-Version: \"%s\" Active-Plan: \"%s\")", instance.Name, instance.Spec.OperatorVersion.Name, activePlanStatus.Name) rootBranchName := tree.AddBranch(rootDisplay) for name, plan := range operator.Spec.Plans { - if name == activePlanType.Spec.PlanName { - planDisplay := fmt.Sprintf("Plan %s (%s strategy) [%s]", name, plan.Strategy, activePlanType.Status.State) + if name == activePlanStatus.Name { + planDisplay := fmt.Sprintf("Plan %s (%s strategy) [%s]", name, plan.Strategy, activePlanStatus.Status) planBranchName := rootBranchName.AddBranch(planDisplay) - for _, phase := range activePlanType.Status.Phases { - phaseDisplay := fmt.Sprintf("Phase %s (%s strategy) [%s]", phase.Name, phase.Strategy, phase.State) + for _, phase := range activePlanStatus.Phases { + phaseDisplay := fmt.Sprintf("Phase %s [%s]", phase.Name, phase.Status) phaseBranchName := planBranchName.AddBranch(phaseDisplay) for _, steps := range phase.Steps { - stepsDisplay := fmt.Sprintf("Step %s (%s)", steps.Name, steps.State) + stepsDisplay := fmt.Sprintf("Step %s (%s)", steps.Name, steps.Status) phaseBranchName.AddBranch(stepsDisplay) } } diff --git a/pkg/kudoctl/util/kudo/kudo.go b/pkg/kudoctl/util/kudo/kudo.go index 5aabc51bc..eca01373f 100644 --- a/pkg/kudoctl/util/kudo/kudo.go +++ b/pkg/kudoctl/util/kudo/kudo.go @@ -59,10 +59,6 @@ func NewClient(namespace, kubeConfigPath string) (*Client, error) { if err != nil { return nil, errors.WithMessage(err, "instances") } - _, err = kudoClientset.KudoV1alpha1().PlanExecutions(namespace).List(v1.ListOptions{}) - if err != nil { - return nil, errors.WithMessage(err, "planexecutions") - } return &Client{ clientset: kudoClientset, diff --git a/pkg/test/harness.go b/pkg/test/harness.go index d22b956e3..6d66ae01d 100644 --- a/pkg/test/harness.go +++ b/pkg/test/harness.go @@ -3,9 +3,6 @@ package test import ( "context" "fmt" - "github.com/kudobuilder/kudo/pkg/controller/instance" - "github.com/kudobuilder/kudo/pkg/controller/operator" - "github.com/kudobuilder/kudo/pkg/controller/operatorversion" "io/ioutil" "math/rand" "os" @@ -14,6 +11,10 @@ import ( "testing" "time" + "github.com/kudobuilder/kudo/pkg/controller/instance" + "github.com/kudobuilder/kudo/pkg/controller/operator" + "github.com/kudobuilder/kudo/pkg/controller/operatorversion" + volumetypes "github.com/docker/docker/api/types/volume" docker "github.com/docker/docker/client" kudo "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" @@ -277,9 +278,9 @@ func (h *Harness) RunKUDO() error { h.logger.Log("Setting up instance controller") err = (&instance.Reconciler{ - Client: mgr.GetClient(), + Client: mgr.GetClient(), Recorder: mgr.GetEventRecorderFor("instance-controller"), - Scheme: mgr.GetScheme(), + Scheme: mgr.GetScheme(), }).SetupWithManager(mgr) if err != nil { h.logger.Log(err, "unable to register instance controller to the manager") From 57b37b55620b984c443233f2ea9b8937a441cbb3 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Mon, 23 Sep 2019 22:04:07 +0200 Subject: [PATCH 10/29] Fix integration test, implement deploy plan trigger --- pkg/apis/kudo/v1alpha1/instance_types.go | 99 ++++++++++++++++++- .../kudo/v1alpha1/zz_generated.deepcopy.go | 6 +- .../instance/instance_controller.go | 39 +++++--- .../instance/plan_execution_engine.go | 31 +++--- pkg/kudoctl/cmd/plan/plan_history.go | 4 +- test/integration/cli-install/00-assert.yaml | 6 +- test/integration/cli-install/01-assert.yaml | 6 +- .../01-assert.yaml | 6 +- .../immutable-client-ip/00-assert.yaml | 3 +- .../immutable-client-ip/01-assert.yaml | 3 +- .../00-assert.yaml | 14 +-- .../01-assert.yaml | 13 ++- .../00-assert.yaml | 14 +-- .../01-assert.yaml | 13 ++- .../00-assert.yaml | 14 +-- .../01-assert.yaml | 13 ++- .../00-assert.yaml | 15 +-- .../01-assert.yaml | 14 +-- .../00-assert.yaml | 15 +-- .../01-assert.yaml | 14 +-- .../00-assert.yaml | 15 +-- .../01-assert.yaml | 14 +-- .../01-assert.yaml | 6 +- .../integration/racy-reconcile/01-assert.yaml | 20 +--- .../00-assert.yaml | 24 ++--- test/integration/step-delete/00-assert.yaml | 3 +- test/integration/step-delete/01-assert.yaml | 3 +- .../update-parameters/00-assert.yaml | 36 ++----- .../update-parameters/01-assert.yaml | 36 ++----- .../update-parameters/02-assert.yaml | 18 ++-- .../upgrade-command/01-assert.yaml | 17 +--- 31 files changed, 295 insertions(+), 239 deletions(-) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index 98c73a352..9691c4dfa 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -16,6 +16,8 @@ limitations under the License. package v1alpha1 import ( + "fmt" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -30,8 +32,8 @@ type InstanceSpec struct { // InstanceStatus defines the observed state of Instance type InstanceStatus struct { - PlanStatus map[string]PlanStatus `json:"planStatus,omitempty"` - AggregatedStatus AggregatedStatus `json:"aggregatedStatus,omitempty"` + PlanStatus []PlanStatus `json:"planStatus,omitempty"` + AggregatedStatus AggregatedStatus `json:"aggregatedStatus,omitempty"` } // AggregatedStatus is overview of an instance status derived from the plan status @@ -51,14 +53,14 @@ type PlanStatus struct { // PhaseStatus is representing status of a phase type PhaseStatus struct { Name string `json:"name,omitempty"` - Status ExecutionStatus `json:"state,omitempty"` + Status ExecutionStatus `json:"status,omitempty"` Steps []StepStatus `json:"steps,omitempty"` } // StepStatus is representing status of a step type StepStatus struct { Name string `json:"name,omitempty"` - Status ExecutionStatus `json:"state,omitempty"` + Status ExecutionStatus `json:"status,omitempty"` } // ExecutionStatus captures the state of the rollout. @@ -79,6 +81,9 @@ const ExecutionError ExecutionStatus = "ERROR" // ExecutionFatalError there was an error deploying the application. const ExecutionFatalError ExecutionStatus = "FATAL_ERROR" +// ExecutionNeverRun is used when this plan/phase/step was never run so far +const ExecutionNeverRun ExecutionStatus = "NEVER_RUN" + // IsTerminal returns true if the status is terminal (either complete, or in a nonrecoverable error) func (s ExecutionStatus) IsTerminal() bool { return s == ExecutionComplete || s == ExecutionFatalError @@ -99,6 +104,92 @@ func (i *Instance) GetPlanInProgress() *PlanStatus { return nil } +// NoPlanEverExecuted returns true is this is new instance for which we never executed any plan +func (i *Instance) NoPlanEverExecuted() bool { + for _, p := range i.Status.PlanStatus { + if p.Status != ExecutionNeverRun { + return false + } + } + return true +} + +// EnsurePlanStatusInitialized initializes plan status for all plans this instance supports +// it does not trigger run of any plan +func (i *Instance) EnsurePlanStatusInitialized(ov *OperatorVersion) { + i.Status.PlanStatus = make([]PlanStatus, 0) + + for planName, plan := range ov.Spec.Plans { + planStatus := &PlanStatus{ + Name: planName, + Status: ExecutionNeverRun, + Phases: make([]PhaseStatus, 0), + } + for _, phase := range plan.Phases { + phaseStatus := &PhaseStatus{ + Name: phase.Name, + Status: ExecutionNeverRun, + Steps: make([]StepStatus, 0), + } + for _, step := range phase.Steps { + phaseStatus.Steps = append(phaseStatus.Steps, StepStatus{ + Name: step.Name, + Status: ExecutionNeverRun, + }) + } + planStatus.Phases = append(planStatus.Phases, *phaseStatus) + } + i.Status.PlanStatus = append(i.Status.PlanStatus, *planStatus) + } +} + +// StartPlanExecution mark plan as to be executed +func (i *Instance) StartPlanExecution(planName string, ov *OperatorVersion) error { + if i.NoPlanEverExecuted() { + i.EnsurePlanStatusInitialized(ov) + } + // TODO: save snapshot of instance + // update status of the instance to reflect the newly startin plan + notFound := true + for i1, v := range i.Status.PlanStatus { + if v.Name == planName { + // update plan status + notFound = false + i.Status.PlanStatus[i1].Status = ExecutionPending + for i2, p := range v.Phases { + i.Status.PlanStatus[i1].Phases[i2].Status = ExecutionPending + for i3, _ := range p.Steps { + i.Status.PlanStatus[i1].Phases[i2].Steps[i3].Status = ExecutionPending + } + } + + // update activePlan and instance status + i.Status.AggregatedStatus.Status = ExecutionPending + i.Status.AggregatedStatus.ActivePlanName = planName + + break + } + } + + if notFound { + return fmt.Errorf("asked to execute a plan %s but no such plan found in instance %s/%s", planName, i.Namespace, i.Name) + } + return nil +} + +// UpdateInstanceStatus updates `Status.PlanStatus` and `Status.AggregatedStatus` property based on the given plan +func (i *Instance) UpdateInstanceStatus(planStatus *PlanStatus) { + for k, v := range i.Status.PlanStatus { + if v.Name == planStatus.Name { + i.Status.PlanStatus[k] = *planStatus + i.Status.AggregatedStatus.Status = planStatus.Status + if planStatus.Status.IsTerminal() { + i.Status.AggregatedStatus.ActivePlanName = "" + } + } + } +} + // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/kudo/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/kudo/v1alpha1/zz_generated.deepcopy.go index 93d9b8b77..0d71b1255 100644 --- a/pkg/apis/kudo/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/kudo/v1alpha1/zz_generated.deepcopy.go @@ -243,9 +243,9 @@ func (in *InstanceStatus) DeepCopyInto(out *InstanceStatus) { *out = *in if in.PlanStatus != nil { in, out := &in.PlanStatus, &out.PlanStatus - *out = make(map[string]PlanStatus, len(*in)) - for key, val := range *in { - (*out)[key] = *val.DeepCopy() + *out = make([]PlanStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) } } out.AggregatedStatus = in.AggregatedStatus diff --git a/pkg/controller/instance/instance_controller.go b/pkg/controller/instance/instance_controller.go index 2948d89a6..a3732dfce 100644 --- a/pkg/controller/instance/instance_controller.go +++ b/pkg/controller/instance/instance_controller.go @@ -77,28 +77,38 @@ func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { return reconcile.Result{}, err // OV not found has to be retried because it can really have been created after I } - // ---------- 2. First check if we should not start execution of new plan because of update ---------- - - // TODO + // ---------- 2. First check if we should start execution of new plan ---------- + + // If this is newly created instance, we need to trigger deploy plan + if instance.NoPlanEverExecuted() { + log.Printf("InstanceController: No plan ever executed for instance %s/%s, going to execute deploy plan", instance.Namespace, instance.Name) + err = instance.StartPlanExecution("deploy", ov) + if err != nil { + return reconcile.Result{}, r.handleError(err, instance) + } + } - // ---------- 3. If we're currently running plan, continue with the execution ---------- + // ---------- 3. If there's currently active plan, continue with the execution ---------- activePlanStatus := instance.GetPlanInProgress() if activePlanStatus == nil { // we have no plan in progress + log.Printf("InstanceController: Nothing to do, no plan in progress for instance %s/%s", instance.Namespace, instance.Name) return reconcile.Result{}, nil } activePlan, metadata, err := preparePlanExecution(instance, ov, activePlanStatus) if err != nil { + fmt.Printf("Pre-error instance %v", instance) err = r.handleError(err, instance) return reconcile.Result{}, err } + log.Printf("InstanceController: Going to proceed in execution of active plan %s on instance %s/%s", activePlan.Name, instance.Namespace, instance.Name) newStatus, err := executePlan(activePlan, metadata, r.Client, &kustomizeEnhancer{r.Scheme}) // ---------- 4. Update status of instance after the execution proceeded ---------- if newStatus != nil { - instance.Status.PlanStatus[activePlan.Name] = *newStatus + instance.UpdateInstanceStatus(newStatus) } if err != nil { err = r.handleError(err, instance) @@ -122,7 +132,7 @@ func preparePlanExecution(instance *kudov1alpha1.Instance, ov *kudov1alpha1.Oper planSpec, ok := ov.Spec.Plans[activePlanStatus.Name] if !ok { - return nil, nil, executionError{fmt.Errorf("could not find required plan (%v)", activePlanStatus.Name), false, kudo.String("InvalidPlan")} + return nil, nil, &executionError{fmt.Errorf("could not find required plan (%v)", activePlanStatus.Name), false, kudo.String("InvalidPlan")} } return &activePlan{ @@ -146,8 +156,16 @@ func preparePlanExecution(instance *kudov1alpha1.Instance, ov *kudov1alpha1.Oper // specify eventReason as nil if you don't wish to publish a warning event // returns err if this err should be retried, nil otherwise func (r *Reconciler) handleError(err error, instance *kudov1alpha1.Instance) error { - // TODO update the execution state log.Printf("InstanceController: %v", err) + + // first update instance as we want to propagate errors also to the `Instance.Status.PlanStatus` + clientErr := r.Client.Update(context.TODO(), instance) + if clientErr != nil { + log.Printf("InstanceController: Error when updating instance state. %v", clientErr) + return clientErr + } + + // determine if retry is necessary based on the error type if exErr, ok := err.(*executionError); ok { if exErr.eventName != nil { r.Recorder.Event(instance, "Warning", kudo.StringValue(exErr.eventName), err.Error()) @@ -168,9 +186,8 @@ func (r *Reconciler) getInstance(request ctrl.Request) (instance *kudov1alpha1.I if err != nil { // Error reading the object - requeue the request. log.Printf("InstanceController: Error getting instance \"%v\": %v", - instance.Name, + request.NamespacedName, err) - r.Recorder.Event(instance, "Warning", "InvalidInstance", fmt.Sprintf("Error getting instance \"%v\": %v", instance.Name, err)) return nil, err } return instance, nil @@ -191,7 +208,7 @@ func (r *Reconciler) getOperatorVersion(instance *kudov1alpha1.Instance) (ov *ku instance.Spec.OperatorVersion.Name, instance.Name, err) - r.Recorder.Event(instance, "Warning", "InvalidOperatorVersion", fmt.Sprintf("Error getting operatorversion \"%v\": %v", ov.Name, err)) + r.Recorder.Event(instance, "Warning", "InvalidOperatorVersion", fmt.Sprintf("Error getting operatorversion \"%v\": %v", instance.Spec.OperatorVersion.Name, err)) return nil, err } return ov, nil @@ -218,7 +235,7 @@ func getParameters(instance *kudov1alpha1.Instance, operatorVersion *kudov1alpha } if len(missingRequiredParameters) != 0 { - return nil, executionError{err: fmt.Errorf("parameters are missing when evaluating template: %s", strings.Join(missingRequiredParameters, ",")), fatal: true, eventName: kudo.String("Missing parameter")} + return nil, &executionError{err: fmt.Errorf("parameters are missing when evaluating template: %s", strings.Join(missingRequiredParameters, ",")), fatal: true, eventName: kudo.String("Missing parameter")} } return params, nil diff --git a/pkg/controller/instance/plan_execution_engine.go b/pkg/controller/instance/plan_execution_engine.go index f3d6a5986..c19c0c66b 100644 --- a/pkg/controller/instance/plan_execution_engine.go +++ b/pkg/controller/instance/plan_execution_engine.go @@ -9,10 +9,12 @@ import ( "k8s.io/apimachinery/pkg/types" + "errors" + "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" kudoengine "github.com/kudobuilder/kudo/pkg/engine" "github.com/kudobuilder/kudo/pkg/util/health" - "github.com/pkg/errors" + errwrap "github.com/pkg/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -54,7 +56,7 @@ type executionMetadata struct { // the next step could consist of actually executing multiple steps of the plan or just one depending on the execution strategy of the phase (serial/parallel) // result of running this function is new state of the execution that is returned to the caller (it can either be completed, or still in progress or errored) // in case of error, error is returned along with the state as well (so that it's possible to report which step caused the error) -// in case of error, method returns both error or fatalError which should indicate unrecoverable error meaning there is no point in retrying that execution +// in case of error, method returns executionError which has property to indicate unrecoverable error meaning if there is no point in retrying that execution func executePlan(plan *activePlan, metadata *executionMetadata, c client.Client, renderer kubernetesObjectEnhancer) (*v1alpha1.PlanStatus, error) { if plan.Status.IsTerminal() { log.Printf("PlanExecution: Plan %s for instance %s is terminal, nothing to do", plan.Name, metadata.instanceName) @@ -67,7 +69,12 @@ func executePlan(plan *activePlan, metadata *executionMetadata, c client.Client, // render kubernetes resources needed to execute this plan planResources, err := prepareKubeResources(plan, metadata, renderer) if err != nil { - newState.Status = v1alpha1.ExecutionError + var exErr *executionError + if errors.As(err, &exErr) { + newState.Status = v1alpha1.ExecutionFatalError + } else { + newState.Status = v1alpha1.ExecutionError + } return newState, err } @@ -260,21 +267,21 @@ func prepareKubeResources(plan *activePlan, meta *executionMetadata, renderer ku if resource, ok := plan.Templates[res]; ok { templatedYaml, err := engine.Render(resource, configs) if err != nil { - phaseState.Status = v1alpha1.ExecutionError - stepState.Status = v1alpha1.ExecutionError + phaseState.Status = v1alpha1.ExecutionFatalError + stepState.Status = v1alpha1.ExecutionFatalError - err := errors.Wrapf(err, "error expanding template") + err := errwrap.Wrapf(err, "error expanding template") log.Print(err) - return nil, executionError{err, true, nil} + return nil, &executionError{err, true, nil} } resourcesAsString[res] = templatedYaml } else { - phaseState.Status = v1alpha1.ExecutionError - stepState.Status = v1alpha1.ExecutionError + phaseState.Status = v1alpha1.ExecutionFatalError + stepState.Status = v1alpha1.ExecutionFatalError err := fmt.Errorf("PlanExecution: Error finding resource named %v for operator version %v", res, meta.operatorVersionName) log.Print(err) - return nil, executionError{err, true, nil} + return nil, &executionError{err, true, nil} } } @@ -294,7 +301,7 @@ func prepareKubeResources(plan *activePlan, meta *executionMetadata, renderer ku stepState.Status = v1alpha1.ExecutionError log.Printf("Error creating Kubernetes objects from step %v in phase %v of plan %v: %v", step.Name, phase.Name, meta.planExecutionID, err) - return nil, err + return nil, &executionError{err, false, nil} } resources = append(resources, resourcesWithConventions...) } else { @@ -303,7 +310,7 @@ func prepareKubeResources(plan *activePlan, meta *executionMetadata, renderer ku err := fmt.Errorf("Error finding task named %s for operator version %s", taskSpec, meta.operatorVersionName) log.Print(err) - return nil, executionError{err, false, nil} + return nil, &executionError{err, false, nil} } } diff --git a/pkg/kudoctl/cmd/plan/plan_history.go b/pkg/kudoctl/cmd/plan/plan_history.go index 2a26ae98f..32ef23f50 100644 --- a/pkg/kudoctl/cmd/plan/plan_history.go +++ b/pkg/kudoctl/cmd/plan/plan_history.go @@ -90,7 +90,7 @@ func planHistory(args []string, options *Options, settings *env.Settings) error tree := treeprint.New() timeLayout := "2006-01-02" - for n, p := range instance.Status.PlanStatus { + for _, p := range instance.Status.PlanStatus { msg := "never run" if p.Status != "" && !p.LastFinishedRun.IsZero() { // plan already finished t := p.LastFinishedRun.Format(timeLayout) @@ -98,7 +98,7 @@ func planHistory(args []string, options *Options, settings *env.Settings) error } else if p.Status.IsRunning() { msg = "is running" } - historyDisplay := fmt.Sprintf("%s (%s)", n, msg) + historyDisplay := fmt.Sprintf("%s (%s)", p.Name, msg) tree.AddBranch(historyDisplay) } diff --git a/test/integration/cli-install/00-assert.yaml b/test/integration/cli-install/00-assert.yaml index 31035c523..eab04bdc9 100644 --- a/test/integration/cli-install/00-assert.yaml +++ b/test/integration/cli-install/00-assert.yaml @@ -7,10 +7,8 @@ spec: operatorVersion: name: cli-install-operator-0.1.0 status: - status: COMPLETE - activePlan: - apiVersion: kudo.dev/v1alpha1 - kind: PlanExecution + aggregatedStatus: + status: COMPLETE --- apiVersion: v1 kind: Service diff --git a/test/integration/cli-install/01-assert.yaml b/test/integration/cli-install/01-assert.yaml index f55499cc8..ff3daeda6 100644 --- a/test/integration/cli-install/01-assert.yaml +++ b/test/integration/cli-install/01-assert.yaml @@ -8,10 +8,8 @@ spec: operatorVersion: name: cli-install-operator-0.1.0 status: - status: COMPLETE - activePlan: - apiVersion: kudo.dev/v1alpha1 - kind: PlanExecution + aggregatedStatus: + status: COMPLETE --- apiVersion: v1 kind: Service diff --git a/test/integration/create-operator-in-operator/01-assert.yaml b/test/integration/create-operator-in-operator/01-assert.yaml index 92ec9f980..94e0f2f2c 100644 --- a/test/integration/create-operator-in-operator/01-assert.yaml +++ b/test/integration/create-operator-in-operator/01-assert.yaml @@ -3,14 +3,16 @@ kind: Instance metadata: name: oio-instance status: - status: COMPLETE + aggregatedStatus: + status: COMPLETE --- apiVersion: kudo.dev/v1alpha1 kind: Instance metadata: name: oio-instance-inner-instance status: - status: COMPLETE + aggregatedStatus: + status: COMPLETE --- apiVersion: apps/v1 kind: Deployment diff --git a/test/integration/immutable-client-ip/00-assert.yaml b/test/integration/immutable-client-ip/00-assert.yaml index 4f0c14448..4049576d2 100644 --- a/test/integration/immutable-client-ip/00-assert.yaml +++ b/test/integration/immutable-client-ip/00-assert.yaml @@ -3,7 +3,8 @@ kind: Instance metadata: name: immutable-client-ip-instance status: - status: COMPLETE + aggregatedStatus: + status: COMPLETE --- apiVersion: v1 kind: Service diff --git a/test/integration/immutable-client-ip/01-assert.yaml b/test/integration/immutable-client-ip/01-assert.yaml index ba642ba26..c826e17bc 100644 --- a/test/integration/immutable-client-ip/01-assert.yaml +++ b/test/integration/immutable-client-ip/01-assert.yaml @@ -3,7 +3,8 @@ kind: Instance metadata: name: immutable-client-ip-instance status: - status: COMPLETE + aggregatedStatus: + status: COMPLETE --- apiVersion: v1 kind: Service diff --git a/test/integration/instance-controller-param-change-custom-trigger/00-assert.yaml b/test/integration/instance-controller-param-change-custom-trigger/00-assert.yaml index d2b0f9d6d..ffeced5fb 100644 --- a/test/integration/instance-controller-param-change-custom-trigger/00-assert.yaml +++ b/test/integration/instance-controller-param-change-custom-trigger/00-assert.yaml @@ -3,12 +3,8 @@ kind: Instance metadata: name: icto-custom-trigger status: - status: COMPLETE ---- -apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution -metadata: - labels: - kudo.dev/instance: icto-custom-trigger -spec: - planName: deploy + aggregatedStatus: + status: COMPLETE + planStatus: + - status: COMPLETE + name: deploy \ No newline at end of file diff --git a/test/integration/instance-controller-param-change-custom-trigger/01-assert.yaml b/test/integration/instance-controller-param-change-custom-trigger/01-assert.yaml index b27bc1dcd..23dbcc83d 100644 --- a/test/integration/instance-controller-param-change-custom-trigger/01-assert.yaml +++ b/test/integration/instance-controller-param-change-custom-trigger/01-assert.yaml @@ -1,7 +1,10 @@ apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution +kind: Instance metadata: - labels: - kudo.dev/instance: icto-custom-trigger -spec: - planName: foo-changed + name: icto-custom-trigger +status: + aggregatedStatus: + status: COMPLETE + planStatus: + - status: COMPLETE + name: foo-changed \ No newline at end of file diff --git a/test/integration/instance-controller-param-change-fallback-to-deploy/00-assert.yaml b/test/integration/instance-controller-param-change-fallback-to-deploy/00-assert.yaml index a7a1a6021..7107048f4 100644 --- a/test/integration/instance-controller-param-change-fallback-to-deploy/00-assert.yaml +++ b/test/integration/instance-controller-param-change-fallback-to-deploy/00-assert.yaml @@ -3,12 +3,8 @@ kind: Instance metadata: name: icto-fallback-to-deploy status: - status: COMPLETE ---- -apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution -metadata: - labels: - kudo.dev/instance: icto-fallback-to-deploy -spec: - planName: deploy + aggregatedStatus: + status: COMPLETE + planStatus: + - name: deploy + status: COMPLETE diff --git a/test/integration/instance-controller-param-change-fallback-to-deploy/01-assert.yaml b/test/integration/instance-controller-param-change-fallback-to-deploy/01-assert.yaml index 90a7259b5..7107048f4 100644 --- a/test/integration/instance-controller-param-change-fallback-to-deploy/01-assert.yaml +++ b/test/integration/instance-controller-param-change-fallback-to-deploy/01-assert.yaml @@ -1,7 +1,10 @@ apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution +kind: Instance metadata: - labels: - kudo.dev/instance: icto-fallback-to-deploy -spec: - planName: deploy + name: icto-fallback-to-deploy +status: + aggregatedStatus: + status: COMPLETE + planStatus: + - name: deploy + status: COMPLETE diff --git a/test/integration/instance-controller-param-change-no-trigger/00-assert.yaml b/test/integration/instance-controller-param-change-no-trigger/00-assert.yaml index e6d6b23fd..93efbe7af 100644 --- a/test/integration/instance-controller-param-change-no-trigger/00-assert.yaml +++ b/test/integration/instance-controller-param-change-no-trigger/00-assert.yaml @@ -3,12 +3,8 @@ kind: Instance metadata: name: icto-no-trigger status: - status: COMPLETE ---- -apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution -metadata: - labels: - kudo.dev/instance: icto-no-trigger -spec: - planName: deploy + aggregatedStatus: + status: COMPLETE + planStatus: + - name: deploy + status: COMPLETE diff --git a/test/integration/instance-controller-param-change-no-trigger/01-assert.yaml b/test/integration/instance-controller-param-change-no-trigger/01-assert.yaml index e5f240e5a..849608e8c 100644 --- a/test/integration/instance-controller-param-change-no-trigger/01-assert.yaml +++ b/test/integration/instance-controller-param-change-no-trigger/01-assert.yaml @@ -1,7 +1,10 @@ apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution +kind: Instance metadata: - labels: - kudo.dev/instance: icto-no-trigger -spec: - planName: update + name: icto-no-trigger +status: + aggregatedStatus: + status: COMPLETE + planStatus: + - name: update + status: COMPLETE diff --git a/test/integration/instance-controller-upgrade-fallback-to-deploy/00-assert.yaml b/test/integration/instance-controller-upgrade-fallback-to-deploy/00-assert.yaml index cfc31dffa..953c2bf0c 100644 --- a/test/integration/instance-controller-upgrade-fallback-to-deploy/00-assert.yaml +++ b/test/integration/instance-controller-upgrade-fallback-to-deploy/00-assert.yaml @@ -3,13 +3,8 @@ kind: Instance metadata: name: icto-upgrade-fallback-to-deploy status: - status: COMPLETE ---- -apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution -metadata: - labels: - kudo.dev/instance: icto-upgrade-fallback-to-deploy - kudo.dev/operator-version: icto-upgrade -spec: - planName: deploy + aggregatedStatus: + status: COMPLETE + planStatus: + - name: deploy + status: COMPLETE diff --git a/test/integration/instance-controller-upgrade-fallback-to-deploy/01-assert.yaml b/test/integration/instance-controller-upgrade-fallback-to-deploy/01-assert.yaml index fde286da7..953c2bf0c 100644 --- a/test/integration/instance-controller-upgrade-fallback-to-deploy/01-assert.yaml +++ b/test/integration/instance-controller-upgrade-fallback-to-deploy/01-assert.yaml @@ -1,8 +1,10 @@ apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution +kind: Instance metadata: - labels: - kudo.dev/instance: icto-upgrade-fallback-to-deploy - kudo.dev/operator-version: icto-upgrade-fallback-to-deploy -spec: - planName: deploy + name: icto-upgrade-fallback-to-deploy +status: + aggregatedStatus: + status: COMPLETE + planStatus: + - name: deploy + status: COMPLETE diff --git a/test/integration/instance-controller-upgrade-fallback-to-update/00-assert.yaml b/test/integration/instance-controller-upgrade-fallback-to-update/00-assert.yaml index e90b1a4de..9e8304aaf 100644 --- a/test/integration/instance-controller-upgrade-fallback-to-update/00-assert.yaml +++ b/test/integration/instance-controller-upgrade-fallback-to-update/00-assert.yaml @@ -3,13 +3,8 @@ kind: Instance metadata: name: icto-upgrade-fallback-to-update status: - status: COMPLETE ---- -apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution -metadata: - labels: - kudo.dev/instance: icto-upgrade-fallback-to-update - kudo.dev/operator-version: icto-upgrade -spec: - planName: deploy + aggregatedStatus: + status: COMPLETE + planStatus: + - name: deploy + status: COMPLETE diff --git a/test/integration/instance-controller-upgrade-fallback-to-update/01-assert.yaml b/test/integration/instance-controller-upgrade-fallback-to-update/01-assert.yaml index b663306da..c4e59cae4 100644 --- a/test/integration/instance-controller-upgrade-fallback-to-update/01-assert.yaml +++ b/test/integration/instance-controller-upgrade-fallback-to-update/01-assert.yaml @@ -1,8 +1,10 @@ apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution +kind: Instance metadata: - labels: - kudo.dev/instance: icto-upgrade-fallback-to-update - kudo.dev/operator-version: icto-upgrade-fallback-to-update -spec: - planName: update + name: icto-upgrade-fallback-to-update +status: + aggregatedStatus: + status: COMPLETE + planStatus: + - name: update + status: COMPLETE diff --git a/test/integration/instance-controller-upgrade/00-assert.yaml b/test/integration/instance-controller-upgrade/00-assert.yaml index 0842ab3bf..a52ddf49d 100644 --- a/test/integration/instance-controller-upgrade/00-assert.yaml +++ b/test/integration/instance-controller-upgrade/00-assert.yaml @@ -3,13 +3,8 @@ kind: Instance metadata: name: icto-upgrade status: - status: COMPLETE ---- -apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution -metadata: - labels: - kudo.dev/instance: icto-upgrade - kudo.dev/operator-version: icto-upgrade -spec: - planName: deploy + aggregatedStatus: + status: COMPLETE + planStatus: + - name: deploy + status: COMPLETE diff --git a/test/integration/instance-controller-upgrade/01-assert.yaml b/test/integration/instance-controller-upgrade/01-assert.yaml index d38d02902..f2d0dc399 100644 --- a/test/integration/instance-controller-upgrade/01-assert.yaml +++ b/test/integration/instance-controller-upgrade/01-assert.yaml @@ -1,8 +1,10 @@ apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution +kind: Instance metadata: - labels: - kudo.dev/instance: icto-upgrade - kudo.dev/operator-version: icto-upgrade-v2 -spec: - planName: upgrade + name: icto-upgrade +status: + aggregatedStatus: + status: COMPLETE + planStatus: + - name: upgrade + status: COMPLETE diff --git a/test/integration/parse-param-operator-in-operator/01-assert.yaml b/test/integration/parse-param-operator-in-operator/01-assert.yaml index 812dbbfc1..44221248e 100644 --- a/test/integration/parse-param-operator-in-operator/01-assert.yaml +++ b/test/integration/parse-param-operator-in-operator/01-assert.yaml @@ -3,14 +3,16 @@ kind: Instance metadata: name: dream1 status: - status: IN_PROGRESS + aggregatedStatus: + status: IN_PROGRESS --- apiVersion: kudo.dev/v1alpha1 kind: Instance metadata: name: dream1-operator status: - status: IN_PROGRESS + aggregatedStatus: + status: IN_PROGRESS --- apiVersion: apps/v1 kind: Deployment diff --git a/test/integration/racy-reconcile/01-assert.yaml b/test/integration/racy-reconcile/01-assert.yaml index 6443879c8..b56af8ec3 100644 --- a/test/integration/racy-reconcile/01-assert.yaml +++ b/test/integration/racy-reconcile/01-assert.yaml @@ -3,18 +3,8 @@ kind: Instance metadata: name: racy-instance status: - status: COMPLETE ---- -apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution -metadata: - labels: - kudo.dev/operator-version: racy-operator - kudo.dev/instance: racy-instance - ownerReferences: - - apiVersion: kudo.dev/v1alpha1 - kind: Instance - name: racy-instance -status: - name: deploy - state: COMPLETE + aggregatedStatus: + status: COMPLETE + planStatus: + - name: deploy + status: COMPLETE diff --git a/test/integration/rendering-error-plan-failed/00-assert.yaml b/test/integration/rendering-error-plan-failed/00-assert.yaml index dcb317dd7..0d6a874d6 100644 --- a/test/integration/rendering-error-plan-failed/00-assert.yaml +++ b/test/integration/rendering-error-plan-failed/00-assert.yaml @@ -1,17 +1,13 @@ apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution +kind: Instance metadata: - labels: - kudo.dev/instance: invalid1 - ownerReferences: - - apiVersion: kudo.dev/v1alpha1 - blockOwnerDeletion: true - controller: true - kind: Instance - name: invalid1 + name: invalid1 status: - name: deploy - state: ERROR - phases: - - name: invalid-phase - state: ERROR + aggregatedStatus: + status: FATAL_ERROR + planStatus: + - name: deploy + status: FATAL_ERROR + phases: + - name: invalid-phase + status: FATAL_ERROR diff --git a/test/integration/step-delete/00-assert.yaml b/test/integration/step-delete/00-assert.yaml index 5352b1a7e..800a5bf63 100644 --- a/test/integration/step-delete/00-assert.yaml +++ b/test/integration/step-delete/00-assert.yaml @@ -4,4 +4,5 @@ kind: Instance metadata: name: step-delete-instance status: - status: COMPLETE + aggregatedStatus: + status: COMPLETE diff --git a/test/integration/step-delete/01-assert.yaml b/test/integration/step-delete/01-assert.yaml index 5352b1a7e..800a5bf63 100644 --- a/test/integration/step-delete/01-assert.yaml +++ b/test/integration/step-delete/01-assert.yaml @@ -4,4 +4,5 @@ kind: Instance metadata: name: step-delete-instance status: - status: COMPLETE + aggregatedStatus: + status: COMPLETE diff --git a/test/integration/update-parameters/00-assert.yaml b/test/integration/update-parameters/00-assert.yaml index 13572ac4c..98831507d 100644 --- a/test/integration/update-parameters/00-assert.yaml +++ b/test/integration/update-parameters/00-assert.yaml @@ -12,30 +12,14 @@ kind: Instance metadata: name: toy1 status: - status: COMPLETE - activePlan: - apiVersion: kudo.dev/v1alpha1 - kind: PlanExecution ---- -apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution -metadata: - labels: - kudo.dev/instance: toy1 - kudo.dev/operator-version: toy-v1 - ownerReferences: - - apiVersion: kudo.dev/v1alpha1 - blockOwnerDeletion: true - controller: true - kind: Instance - name: toy1 -status: - name: deploy - phases: - - name: deploy - state: COMPLETE - steps: + aggregatedStatus: + status: COMPLETE + planStatus: - name: deploy - state: COMPLETE - state: COMPLETE - strategy: serial + status: COMPLETE + phases: + - name: deploy + status: COMPLETE + steps: + - name: deploy + status: COMPLETE diff --git a/test/integration/update-parameters/01-assert.yaml b/test/integration/update-parameters/01-assert.yaml index ef51d6d76..9fe72b1cd 100644 --- a/test/integration/update-parameters/01-assert.yaml +++ b/test/integration/update-parameters/01-assert.yaml @@ -12,30 +12,14 @@ kind: Instance metadata: name: toy1 status: - status: COMPLETE - activePlan: - apiVersion: kudo.dev/v1alpha1 - kind: PlanExecution ---- -apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution -metadata: - labels: - kudo.dev/instance: toy1 - kudo.dev/operator-version: toy-v1 - ownerReferences: - - apiVersion: kudo.dev/v1alpha1 - blockOwnerDeletion: true - controller: true - kind: Instance - name: toy1 -status: - name: scale - phases: - - name: scale - state: COMPLETE - steps: + aggregatedStatus: + status: COMPLETE + planStatus: - name: scale - state: COMPLETE - state: COMPLETE - strategy: serial + status: COMPLETE + phases: + - name: scale + status: COMPLETE + steps: + - name: scale + status: COMPLETE diff --git a/test/integration/update-parameters/02-assert.yaml b/test/integration/update-parameters/02-assert.yaml index 8d6002a55..23d8b5825 100644 --- a/test/integration/update-parameters/02-assert.yaml +++ b/test/integration/update-parameters/02-assert.yaml @@ -7,13 +7,15 @@ spec: param: "after" --- apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution +kind: Instance metadata: - labels: - kudo.dev/instance: toy1 - kudo.dev/operator-version: toy-v1 + name: toy1 status: - name: update - phases: - - name: update - state: COMPLETE + aggregatedStatus: + status: COMPLETE + planStatus: + - name: update + status: COMPLETE + phases: + - name: update + status: COMPLETE diff --git a/test/integration/upgrade-command/01-assert.yaml b/test/integration/upgrade-command/01-assert.yaml index b5f343679..0903ef184 100644 --- a/test/integration/upgrade-command/01-assert.yaml +++ b/test/integration/upgrade-command/01-assert.yaml @@ -8,15 +8,8 @@ spec: parameters: replicas: "4" status: - activePlan: {} ---- -apiVersion: kudo.dev/v1alpha1 -kind: PlanExecution -metadata: - labels: - kudo.dev/operator-version: upgrade-operator-0.2.0 - ownerReferences: - - kind: Instance - name: upgrade-instance -status: - name: upgrade \ No newline at end of file + aggregatedStatus: + status: COMPLETE + planStatus: + - name: upgrade + status: COMPLETE \ No newline at end of file From 123f4963333d76f3775eb022ee3bc59024f10198 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Tue, 24 Sep 2019 19:36:37 +0200 Subject: [PATCH 11/29] Upgrade --- pkg/apis/kudo/v1alpha1/instance_types.go | 90 ++++++++++++++++++- .../instance/instance_controller.go | 10 ++- .../instance/plan_execution_engine.go | 2 +- .../instance/plan_execution_engine_test.go | 2 +- .../00-assert.yaml | 4 +- .../01-assert.yaml | 4 +- .../00-assert.yaml | 1 + .../01-assert.yaml | 1 + .../00-assert.yaml | 1 + .../01-assert.yaml | 1 + .../00-assert.yaml | 2 + .../00-assert.yaml | 1 + .../01-assert.yaml | 1 + .../update-parameters/00-assert.yaml | 2 + .../update-parameters/01-assert.yaml | 2 + .../update-parameters/02-assert.yaml | 2 + .../upgrade-command/00-assert.yaml | 10 +++ 17 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 test/integration/upgrade-command/00-assert.yaml diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index 9691c4dfa..947d595a8 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -16,7 +16,10 @@ limitations under the License. package v1alpha1 import ( + "encoding/json" "fmt" + "github.com/kudobuilder/kudo/pkg/util/kudo" + "reflect" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -148,8 +151,8 @@ func (i *Instance) StartPlanExecution(planName string, ov *OperatorVersion) erro if i.NoPlanEverExecuted() { i.EnsurePlanStatusInitialized(ov) } - // TODO: save snapshot of instance - // update status of the instance to reflect the newly startin plan + + // update status of the instance to reflect the newly starting plan notFound := true for i1, v := range i.Status.PlanStatus { if v.Name == planName { @@ -158,7 +161,7 @@ func (i *Instance) StartPlanExecution(planName string, ov *OperatorVersion) erro i.Status.PlanStatus[i1].Status = ExecutionPending for i2, p := range v.Phases { i.Status.PlanStatus[i1].Phases[i2].Status = ExecutionPending - for i3, _ := range p.Steps { + for i3 := range p.Steps { i.Status.PlanStatus[i1].Phases[i2].Steps[i3].Status = ExecutionPending } } @@ -170,10 +173,18 @@ func (i *Instance) StartPlanExecution(planName string, ov *OperatorVersion) erro break } } - if notFound { return fmt.Errorf("asked to execute a plan %s but no such plan found in instance %s/%s", planName, i.Namespace, i.Name) } + + // save snapshot prior to execution for plans requiring snapshot + if planName == "deploy" || planName == "update" || planName == "upgrade" { + err := i.SaveSnapshot() + if err != nil { + return err + } + } + return nil } @@ -190,6 +201,77 @@ func (i *Instance) UpdateInstanceStatus(planStatus *PlanStatus) { } } +const snapshotAnnotation = "kudo.dev/last-applied-instance-state" + +// SaveSnapshot stores the current spec of Instance into the snapshot annotation +// this information is used when executing update/upgrade plans, this overrides any snapshot that existed before +func (i *Instance) SaveSnapshot() error { + jsonBytes, err := json.Marshal(i.Spec) + if err != nil { + return err + } + if i.Annotations == nil { + i.Annotations = make(map[string]string) + } + i.Annotations[snapshotAnnotation] = string(jsonBytes) + return nil +} + +func (i *Instance) getSnapshotedSpec() (*InstanceSpec, error) { + if i.Annotations != nil { + snapshot, ok := i.Annotations[snapshotAnnotation] + if ok { + var spec *InstanceSpec = nil + err := json.Unmarshal([]byte(snapshot), &spec) + if err != nil { + return nil, err + } + return spec, nil + } + } + return nil, nil +} + +func selectPlan(possiblePlans []string, ov *OperatorVersion) *string { + for _, n := range possiblePlans { + if _, ok := ov.Spec.Plans[n]; ok { + return kudo.String(n) + } + } + return nil +} + +// GetPlanToBeExecuted returns name of the plan that should be executed +func (i *Instance) GetPlanToBeExecuted(ov *OperatorVersion) (*string, error) { + if i.GetPlanInProgress() != nil { // we're already running some plan + return nil, nil + } + + // new instance, need to run deploy plan + if i.NoPlanEverExecuted() { + return kudo.String("deploy"), nil + } + + // did the instance change so that we need to run deploy/upgrade/update plan? + instanceSnapshot, err := i.getSnapshotedSpec() + if err != nil { + return nil, err + } + if instanceSnapshot.OperatorVersion.Name != i.Spec.OperatorVersion.Name || instanceSnapshot.OperatorVersion.Namespace != i.Spec.OperatorVersion.Namespace { + // this instance was upgraded + plan := selectPlan([]string{"upgrade", "update", "deploy"}, ov) + if plan == nil { + return nil, fmt.Errorf("supposed to execute plan because instance %s/%s was upgraded but none of the deploy, upgrade, update plans found in linked operatorVersion", i.Namespace, i.Name) + } + return plan, nil + } + if reflect.DeepEqual(instanceSnapshot.Parameters, i.Spec.Parameters) { + // instance updated + } + // make sure that plan exists in OV + return nil, nil +} + // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/controller/instance/instance_controller.go b/pkg/controller/instance/instance_controller.go index a3732dfce..e8f82ab74 100644 --- a/pkg/controller/instance/instance_controller.go +++ b/pkg/controller/instance/instance_controller.go @@ -79,10 +79,12 @@ func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { // ---------- 2. First check if we should start execution of new plan ---------- - // If this is newly created instance, we need to trigger deploy plan - if instance.NoPlanEverExecuted() { - log.Printf("InstanceController: No plan ever executed for instance %s/%s, going to execute deploy plan", instance.Namespace, instance.Name) - err = instance.StartPlanExecution("deploy", ov) + planToBeExecuted, err := instance.GetPlanToBeExecuted(ov) + if err != nil { + return reconcile.Result{}, err + } + if planToBeExecuted != nil { + err = instance.StartPlanExecution(kudo.StringValue(planToBeExecuted), ov) if err != nil { return reconcile.Result{}, r.handleError(err, instance) } diff --git a/pkg/controller/instance/plan_execution_engine.go b/pkg/controller/instance/plan_execution_engine.go index c19c0c66b..61b7544b5 100644 --- a/pkg/controller/instance/plan_execution_engine.go +++ b/pkg/controller/instance/plan_execution_engine.go @@ -87,6 +87,7 @@ func executePlan(plan *activePlan, metadata *executionMetadata, c client.Client, log.Printf("PlanExecution: Phase %s on plan %s and instance %s is in state %s, nothing to do", ph.Name, plan.Name, metadata.instanceName, currentPhaseState.Status) continue } else if isInProgress(currentPhaseState.Status) { + newState.Status = v1alpha1.ExecutionInProgress currentPhaseState.Status = v1alpha1.ExecutionInProgress log.Printf("PlanExecution: Executing phase %s on plan %s and instance %s - it's in progress", ph.Name, plan.Name, metadata.instanceName) @@ -95,7 +96,6 @@ func executePlan(plan *activePlan, metadata *executionMetadata, c client.Client, for _, st := range ph.Steps { currentStepState, _ := getStepFromStatus(st.Name, currentPhaseState) resources := planResources.PhaseResources[ph.Name].StepResources[st.Name] - log.Printf("Resources count: %d", len(resources)) log.Printf("PlanExecution: Executing step %s on plan %s and instance %s - it's in %s state", st.Name, plan.Name, metadata.instanceName, currentStepState.Status) err := executeStep(st, currentStepState, resources, c) diff --git a/pkg/controller/instance/plan_execution_engine_test.go b/pkg/controller/instance/plan_execution_engine_test.go index f614f4dae..79267427e 100644 --- a/pkg/controller/instance/plan_execution_engine_test.go +++ b/pkg/controller/instance/plan_execution_engine_test.go @@ -58,7 +58,7 @@ func TestExecutePlan(t *testing.T) { Tasks: map[string]v1alpha1.TaskSpec{"task": {Resources: []string{"job"}}}, Templates: map[string]string{"job": getResourceAsString(getJob("job1", "default"))}, }, defaultMetadata, &v1alpha1.PlanStatus{ - Status: v1alpha1.ExecutionPending, + Status: v1alpha1.ExecutionInProgress, Name: "test", Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionInProgress, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionInProgress, Name: "step"}}}}, }}, diff --git a/test/integration/instance-controller-param-change-custom-trigger/00-assert.yaml b/test/integration/instance-controller-param-change-custom-trigger/00-assert.yaml index ffeced5fb..40b4ad9f7 100644 --- a/test/integration/instance-controller-param-change-custom-trigger/00-assert.yaml +++ b/test/integration/instance-controller-param-change-custom-trigger/00-assert.yaml @@ -7,4 +7,6 @@ status: status: COMPLETE planStatus: - status: COMPLETE - name: deploy \ No newline at end of file + name: deploy + - name: update + - name: foo-changed \ No newline at end of file diff --git a/test/integration/instance-controller-param-change-custom-trigger/01-assert.yaml b/test/integration/instance-controller-param-change-custom-trigger/01-assert.yaml index 23dbcc83d..13c2008f2 100644 --- a/test/integration/instance-controller-param-change-custom-trigger/01-assert.yaml +++ b/test/integration/instance-controller-param-change-custom-trigger/01-assert.yaml @@ -7,4 +7,6 @@ status: status: COMPLETE planStatus: - status: COMPLETE - name: foo-changed \ No newline at end of file + name: foo-changed + - name: update + - name: deploy \ No newline at end of file diff --git a/test/integration/instance-controller-param-change-fallback-to-deploy/00-assert.yaml b/test/integration/instance-controller-param-change-fallback-to-deploy/00-assert.yaml index 7107048f4..76403a0df 100644 --- a/test/integration/instance-controller-param-change-fallback-to-deploy/00-assert.yaml +++ b/test/integration/instance-controller-param-change-fallback-to-deploy/00-assert.yaml @@ -8,3 +8,4 @@ status: planStatus: - name: deploy status: COMPLETE + - name: upgrade \ No newline at end of file diff --git a/test/integration/instance-controller-param-change-fallback-to-deploy/01-assert.yaml b/test/integration/instance-controller-param-change-fallback-to-deploy/01-assert.yaml index 7107048f4..9c7bd3435 100644 --- a/test/integration/instance-controller-param-change-fallback-to-deploy/01-assert.yaml +++ b/test/integration/instance-controller-param-change-fallback-to-deploy/01-assert.yaml @@ -8,3 +8,4 @@ status: planStatus: - name: deploy status: COMPLETE + - name: upgrade diff --git a/test/integration/instance-controller-param-change-no-trigger/00-assert.yaml b/test/integration/instance-controller-param-change-no-trigger/00-assert.yaml index 93efbe7af..481c033ef 100644 --- a/test/integration/instance-controller-param-change-no-trigger/00-assert.yaml +++ b/test/integration/instance-controller-param-change-no-trigger/00-assert.yaml @@ -8,3 +8,4 @@ status: planStatus: - name: deploy status: COMPLETE + - name: update diff --git a/test/integration/instance-controller-param-change-no-trigger/01-assert.yaml b/test/integration/instance-controller-param-change-no-trigger/01-assert.yaml index 849608e8c..52d63dd24 100644 --- a/test/integration/instance-controller-param-change-no-trigger/01-assert.yaml +++ b/test/integration/instance-controller-param-change-no-trigger/01-assert.yaml @@ -8,3 +8,4 @@ status: planStatus: - name: update status: COMPLETE + - name: deploy diff --git a/test/integration/instance-controller-upgrade-fallback-to-update/00-assert.yaml b/test/integration/instance-controller-upgrade-fallback-to-update/00-assert.yaml index 9e8304aaf..6848a3a81 100644 --- a/test/integration/instance-controller-upgrade-fallback-to-update/00-assert.yaml +++ b/test/integration/instance-controller-upgrade-fallback-to-update/00-assert.yaml @@ -8,3 +8,5 @@ status: planStatus: - name: deploy status: COMPLETE + - name: upgrade + - name: update diff --git a/test/integration/instance-controller-upgrade/00-assert.yaml b/test/integration/instance-controller-upgrade/00-assert.yaml index a52ddf49d..7be596645 100644 --- a/test/integration/instance-controller-upgrade/00-assert.yaml +++ b/test/integration/instance-controller-upgrade/00-assert.yaml @@ -8,3 +8,4 @@ status: planStatus: - name: deploy status: COMPLETE + - name: upgrade diff --git a/test/integration/instance-controller-upgrade/01-assert.yaml b/test/integration/instance-controller-upgrade/01-assert.yaml index f2d0dc399..06b984068 100644 --- a/test/integration/instance-controller-upgrade/01-assert.yaml +++ b/test/integration/instance-controller-upgrade/01-assert.yaml @@ -8,3 +8,4 @@ status: planStatus: - name: upgrade status: COMPLETE + - name: upgrade diff --git a/test/integration/update-parameters/00-assert.yaml b/test/integration/update-parameters/00-assert.yaml index 98831507d..b417df97e 100644 --- a/test/integration/update-parameters/00-assert.yaml +++ b/test/integration/update-parameters/00-assert.yaml @@ -15,6 +15,8 @@ status: aggregatedStatus: status: COMPLETE planStatus: + - name: scale + - name: update - name: deploy status: COMPLETE phases: diff --git a/test/integration/update-parameters/01-assert.yaml b/test/integration/update-parameters/01-assert.yaml index 9fe72b1cd..a067a49e4 100644 --- a/test/integration/update-parameters/01-assert.yaml +++ b/test/integration/update-parameters/01-assert.yaml @@ -15,6 +15,8 @@ status: aggregatedStatus: status: COMPLETE planStatus: + - name: deploy + - name: update - name: scale status: COMPLETE phases: diff --git a/test/integration/update-parameters/02-assert.yaml b/test/integration/update-parameters/02-assert.yaml index 23d8b5825..d6af1cb2f 100644 --- a/test/integration/update-parameters/02-assert.yaml +++ b/test/integration/update-parameters/02-assert.yaml @@ -14,6 +14,8 @@ status: aggregatedStatus: status: COMPLETE planStatus: + - name: scale + - name: deploy - name: update status: COMPLETE phases: diff --git a/test/integration/upgrade-command/00-assert.yaml b/test/integration/upgrade-command/00-assert.yaml new file mode 100644 index 000000000..572c4e666 --- /dev/null +++ b/test/integration/upgrade-command/00-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: kudo.dev/v1alpha1 +kind: Instance +metadata: + name: upgrade-instance +spec: + operatorVersion: + name: upgrade-operator-0.1.0 +status: + aggregatedStatus: + status: COMPLETE \ No newline at end of file From d6d0a40ee3ed46c8bcfc802c3be1a5b40abe5fed Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Tue, 24 Sep 2019 20:13:36 +0200 Subject: [PATCH 12/29] Update --- pkg/apis/kudo/v1alpha1/instance_types.go | 61 ++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index 947d595a8..e003a8d5f 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -18,9 +18,10 @@ package v1alpha1 import ( "encoding/json" "fmt" - "github.com/kudobuilder/kudo/pkg/util/kudo" "reflect" + "github.com/kudobuilder/kudo/pkg/util/kudo" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -221,7 +222,7 @@ func (i *Instance) getSnapshotedSpec() (*InstanceSpec, error) { if i.Annotations != nil { snapshot, ok := i.Annotations[snapshotAnnotation] if ok { - var spec *InstanceSpec = nil + var spec *InstanceSpec err := json.Unmarshal([]byte(snapshot), &spec) if err != nil { return nil, err @@ -232,6 +233,7 @@ func (i *Instance) getSnapshotedSpec() (*InstanceSpec, error) { return nil, nil } +// selectPlan returns nil if none of the plan exists, otherwise the first one in list that exists func selectPlan(possiblePlans []string, ov *OperatorVersion) *string { for _, n := range possiblePlans { if _, ok := ov.Spec.Plans[n]; ok { @@ -258,7 +260,7 @@ func (i *Instance) GetPlanToBeExecuted(ov *OperatorVersion) (*string, error) { return nil, err } if instanceSnapshot.OperatorVersion.Name != i.Spec.OperatorVersion.Name || instanceSnapshot.OperatorVersion.Namespace != i.Spec.OperatorVersion.Namespace { - // this instance was upgraded + // this instance was upgraded to newer version plan := selectPlan([]string{"upgrade", "update", "deploy"}, ov) if plan == nil { return nil, fmt.Errorf("supposed to execute plan because instance %s/%s was upgraded but none of the deploy, upgrade, update plans found in linked operatorVersion", i.Namespace, i.Name) @@ -267,11 +269,62 @@ func (i *Instance) GetPlanToBeExecuted(ov *OperatorVersion) (*string, error) { } if reflect.DeepEqual(instanceSnapshot.Parameters, i.Spec.Parameters) { // instance updated + paramDiff := parameterDifference(instanceSnapshot.Parameters, i.Spec.Parameters) + paramDefinitions := getParamDefinitions(paramDiff, ov) + plan := planNameFromParameters(paramDefinitions, ov) + if plan == nil { + return nil, fmt.Errorf("supposed to execute plan because instance %s/%s was updatet but none of the deploy, update plans found in linked operatorVersion", i.Namespace, i.Name) + } + return plan, nil } - // make sure that plan exists in OV return nil, nil } +// planNameFromParameters determines what plan to run based on params that changed and the related trigger plans +func planNameFromParameters(params []Parameter, ov *OperatorVersion) *string { + for _, p := range params { + // TODO: if the params have different trigger plans, we always select first here which might not be ideal + if p.Trigger != "" && selectPlan([]string{p.Trigger}, ov) != nil { + return kudo.String(p.Trigger) + } + } + return selectPlan([]string{"update", "deploy"}, ov) +} + +// getParamDefinitions retrieves parameter metadata from OperatorVersion CRD +func getParamDefinitions(params map[string]string, ov *OperatorVersion) []Parameter { + defs := make([]Parameter, 0) + for p1 := range params { + for _, p2 := range ov.Spec.Parameters { + if p2.Name == p1 { + defs = append(defs, p2) + } + } + } + return defs +} + +// parameterDifference returns map containing all parameters that were removed or changed between old and new +func parameterDifference(old, new map[string]string) map[string]string { + diff := make(map[string]string) + + for key, val := range old { + // If a parameter was removed in the new spec + if _, ok := new[key]; !ok { + diff[key] = val + } + } + + for key, val := range new { + // If new spec parameter was added or changed + if v, ok := old[key]; !ok || v != val { + diff[key] = val + } + } + + return diff +} + // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object From b2e23c08947499110030ea0ac764811cb1462176 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Wed, 25 Sep 2019 12:02:11 +0200 Subject: [PATCH 13/29] OV reconciliation and update fix --- pkg/apis/kudo/v1alpha1/instance_types.go | 5 ++- .../instance/instance_controller.go | 34 ++++++++++++++++++- ...-upgrade.yaml => 01-update_parameter.yaml} | 0 .../01-assert.yaml | 4 +-- 4 files changed, 39 insertions(+), 4 deletions(-) rename test/integration/immutable-client-ip/{01-upgrade.yaml => 01-update_parameter.yaml} (100%) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index e003a8d5f..b6d9853b7 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -18,6 +18,7 @@ package v1alpha1 import ( "encoding/json" "fmt" + "log" "reflect" "github.com/kudobuilder/kudo/pkg/util/kudo" @@ -261,14 +262,16 @@ func (i *Instance) GetPlanToBeExecuted(ov *OperatorVersion) (*string, error) { } if instanceSnapshot.OperatorVersion.Name != i.Spec.OperatorVersion.Name || instanceSnapshot.OperatorVersion.Namespace != i.Spec.OperatorVersion.Namespace { // this instance was upgraded to newer version + log.Printf("Instance: instance %s/%s was upgraded from %s to %s operatorversion", i.Namespace, i.Name, instanceSnapshot.OperatorVersion.Name, i.Spec.OperatorVersion.Name) plan := selectPlan([]string{"upgrade", "update", "deploy"}, ov) if plan == nil { return nil, fmt.Errorf("supposed to execute plan because instance %s/%s was upgraded but none of the deploy, upgrade, update plans found in linked operatorVersion", i.Namespace, i.Name) } return plan, nil } - if reflect.DeepEqual(instanceSnapshot.Parameters, i.Spec.Parameters) { + if !reflect.DeepEqual(instanceSnapshot.Parameters, i.Spec.Parameters) { // instance updated + log.Printf("Instance: instance %s/%s has updated parameters from %v to %v", i.Namespace, i.Name, instanceSnapshot.Parameters, i.Spec.Parameters) paramDiff := parameterDifference(instanceSnapshot.Parameters, i.Spec.Parameters) paramDefinitions := getParamDefinitions(paramDiff, ov) plan := planNameFromParameters(paramDefinitions, ov) diff --git a/pkg/controller/instance/instance_controller.go b/pkg/controller/instance/instance_controller.go index e8f82ab74..b907b5244 100644 --- a/pkg/controller/instance/instance_controller.go +++ b/pkg/controller/instance/instance_controller.go @@ -19,6 +19,8 @@ import ( "context" "fmt" "log" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/source" "strings" "k8s.io/apimachinery/pkg/runtime" @@ -46,13 +48,42 @@ type Reconciler struct { // SetupWithManager registers this reconciler with the controller manager func (r *Reconciler) SetupWithManager( mgr ctrl.Manager) error { + ovEventHandler := handler.ToRequestsFunc( + func(a handler.MapObject) []reconcile.Request { + requests := make([]reconcile.Request, 0) + instances := &kudov1alpha1.InstanceList{} + // we are listing all instances here, which could come with some performance penalty + // a possible optimization is to introduce filtering based on operatorversion (or operator) + err := mgr.GetClient().List( + context.TODO(), + instances, + ) + if err != nil { + log.Printf("InstanceController: Error fetching instances list for operator %v: %v", a.Meta.GetName(), err) + return nil + } + for _, instance := range instances.Items { + // Sanity check - lets make sure that this instance references the operatorVersion + if instance.Spec.OperatorVersion.Name == a.Meta.GetName() && + instance.GetOperatorVersionNamespace() == a.Meta.GetNamespace() { + requests = append(requests, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: instance.Name, + Namespace: instance.Namespace, + }, + }) + } + } + return requests + }) + return ctrl.NewControllerManagedBy(mgr). For(&kudov1alpha1.Instance{}). Owns(&kudov1alpha1.Instance{}). Owns(&appsv1.Deployment{}). Owns(&batchv1.Job{}). Owns(&appsv1.StatefulSet{}). - Complete(r) + Watches(&source.Kind{Type: &kudov1alpha1.OperatorVersion{}}, &handler.EnqueueRequestsFromMapFunc{ToRequests: ovEventHandler}).Complete(r) } // Reconcile ... @@ -84,6 +115,7 @@ func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { return reconcile.Result{}, err } if planToBeExecuted != nil { + log.Printf("InstanceController: Going to start execution of plan %s on instance %s/%s", kudo.StringValue(planToBeExecuted), instance.Namespace, instance.Name) err = instance.StartPlanExecution(kudo.StringValue(planToBeExecuted), ov) if err != nil { return reconcile.Result{}, r.handleError(err, instance) diff --git a/test/integration/immutable-client-ip/01-upgrade.yaml b/test/integration/immutable-client-ip/01-update_parameter.yaml similarity index 100% rename from test/integration/immutable-client-ip/01-upgrade.yaml rename to test/integration/immutable-client-ip/01-update_parameter.yaml diff --git a/test/integration/instance-controller-upgrade/01-assert.yaml b/test/integration/instance-controller-upgrade/01-assert.yaml index 06b984068..a9f310716 100644 --- a/test/integration/instance-controller-upgrade/01-assert.yaml +++ b/test/integration/instance-controller-upgrade/01-assert.yaml @@ -6,6 +6,6 @@ status: aggregatedStatus: status: COMPLETE planStatus: + - name: deploy - name: upgrade - status: COMPLETE - - name: upgrade + status: COMPLETE \ No newline at end of file From 0213ef6f3abbd754991bfa8e2b43aee2adb92a46 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Wed, 25 Sep 2019 12:19:30 +0200 Subject: [PATCH 14/29] Go back to maps from slice of statuses --- pkg/apis/kudo/v1alpha1/instance_types.go | 20 +++++++++++-------- .../kudo/v1alpha1/zz_generated.deepcopy.go | 6 +++--- .../instance/instance_controller.go | 3 ++- .../00-assert.yaml | 6 ++---- .../01-assert.yaml | 6 ++---- .../00-assert.yaml | 5 ++--- .../01-assert.yaml | 3 +-- .../00-assert.yaml | 3 +-- .../01-assert.yaml | 3 +-- .../00-assert.yaml | 2 +- .../01-assert.yaml | 2 +- .../00-assert.yaml | 4 +--- .../01-assert.yaml | 2 +- .../00-assert.yaml | 3 +-- .../01-assert.yaml | 3 +-- .../integration/racy-reconcile/01-assert.yaml | 2 +- .../00-assert.yaml | 2 +- .../update-parameters/00-assert.yaml | 4 +--- .../update-parameters/01-assert.yaml | 4 +--- .../update-parameters/02-assert.yaml | 4 +--- .../upgrade-command/01-assert.yaml | 2 +- 21 files changed, 38 insertions(+), 51 deletions(-) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index b6d9853b7..3e077f1dc 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -37,8 +37,9 @@ type InstanceSpec struct { // InstanceStatus defines the observed state of Instance type InstanceStatus struct { - PlanStatus []PlanStatus `json:"planStatus,omitempty"` - AggregatedStatus AggregatedStatus `json:"aggregatedStatus,omitempty"` + // slice would be enough here but we cannot use slice because order of sequence in yaml is considered significant while here it's not + PlanStatus map[string]PlanStatus `json:"planStatus,omitempty"` + AggregatedStatus AggregatedStatus `json:"aggregatedStatus,omitempty"` } // AggregatedStatus is overview of an instance status derived from the plan status @@ -122,7 +123,7 @@ func (i *Instance) NoPlanEverExecuted() bool { // EnsurePlanStatusInitialized initializes plan status for all plans this instance supports // it does not trigger run of any plan func (i *Instance) EnsurePlanStatusInitialized(ov *OperatorVersion) { - i.Status.PlanStatus = make([]PlanStatus, 0) + i.Status.PlanStatus = make(map[string]PlanStatus) for planName, plan := range ov.Spec.Plans { planStatus := &PlanStatus{ @@ -144,7 +145,7 @@ func (i *Instance) EnsurePlanStatusInitialized(ov *OperatorVersion) { } planStatus.Phases = append(planStatus.Phases, *phaseStatus) } - i.Status.PlanStatus = append(i.Status.PlanStatus, *planStatus) + i.Status.PlanStatus[planName] = *planStatus } } @@ -156,18 +157,21 @@ func (i *Instance) StartPlanExecution(planName string, ov *OperatorVersion) erro // update status of the instance to reflect the newly starting plan notFound := true - for i1, v := range i.Status.PlanStatus { + for n1, v := range i.Status.PlanStatus { if v.Name == planName { // update plan status notFound = false - i.Status.PlanStatus[i1].Status = ExecutionPending + planStatus := i.Status.PlanStatus[n1] + planStatus.Status = ExecutionPending for i2, p := range v.Phases { - i.Status.PlanStatus[i1].Phases[i2].Status = ExecutionPending + planStatus.Phases[i2].Status = ExecutionPending for i3 := range p.Steps { - i.Status.PlanStatus[i1].Phases[i2].Steps[i3].Status = ExecutionPending + i.Status.PlanStatus[n1].Phases[i2].Steps[i3].Status = ExecutionPending } } + i.Status.PlanStatus[n1] = planStatus // we cannot modify item in map, we need to reassign here + // update activePlan and instance status i.Status.AggregatedStatus.Status = ExecutionPending i.Status.AggregatedStatus.ActivePlanName = planName diff --git a/pkg/apis/kudo/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/kudo/v1alpha1/zz_generated.deepcopy.go index 0d71b1255..93d9b8b77 100644 --- a/pkg/apis/kudo/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/kudo/v1alpha1/zz_generated.deepcopy.go @@ -243,9 +243,9 @@ func (in *InstanceStatus) DeepCopyInto(out *InstanceStatus) { *out = *in if in.PlanStatus != nil { in, out := &in.PlanStatus, &out.PlanStatus - *out = make([]PlanStatus, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) + *out = make(map[string]PlanStatus, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() } } out.AggregatedStatus = in.AggregatedStatus diff --git a/pkg/controller/instance/instance_controller.go b/pkg/controller/instance/instance_controller.go index b907b5244..2d1906c3b 100644 --- a/pkg/controller/instance/instance_controller.go +++ b/pkg/controller/instance/instance_controller.go @@ -19,9 +19,10 @@ import ( "context" "fmt" "log" + "strings" + "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/source" - "strings" "k8s.io/apimachinery/pkg/runtime" diff --git a/test/integration/instance-controller-param-change-custom-trigger/00-assert.yaml b/test/integration/instance-controller-param-change-custom-trigger/00-assert.yaml index 40b4ad9f7..68a617ac8 100644 --- a/test/integration/instance-controller-param-change-custom-trigger/00-assert.yaml +++ b/test/integration/instance-controller-param-change-custom-trigger/00-assert.yaml @@ -6,7 +6,5 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - status: COMPLETE - name: deploy - - name: update - - name: foo-changed \ No newline at end of file + deploy: + status: COMPLETE \ No newline at end of file diff --git a/test/integration/instance-controller-param-change-custom-trigger/01-assert.yaml b/test/integration/instance-controller-param-change-custom-trigger/01-assert.yaml index 13c2008f2..7c4b608c9 100644 --- a/test/integration/instance-controller-param-change-custom-trigger/01-assert.yaml +++ b/test/integration/instance-controller-param-change-custom-trigger/01-assert.yaml @@ -6,7 +6,5 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - status: COMPLETE - name: foo-changed - - name: update - - name: deploy \ No newline at end of file + foo-changed: + status: COMPLETE \ No newline at end of file diff --git a/test/integration/instance-controller-param-change-fallback-to-deploy/00-assert.yaml b/test/integration/instance-controller-param-change-fallback-to-deploy/00-assert.yaml index 76403a0df..c5aff12f8 100644 --- a/test/integration/instance-controller-param-change-fallback-to-deploy/00-assert.yaml +++ b/test/integration/instance-controller-param-change-fallback-to-deploy/00-assert.yaml @@ -6,6 +6,5 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: deploy - status: COMPLETE - - name: upgrade \ No newline at end of file + deploy: + status: COMPLETE \ No newline at end of file diff --git a/test/integration/instance-controller-param-change-fallback-to-deploy/01-assert.yaml b/test/integration/instance-controller-param-change-fallback-to-deploy/01-assert.yaml index 9c7bd3435..100121f72 100644 --- a/test/integration/instance-controller-param-change-fallback-to-deploy/01-assert.yaml +++ b/test/integration/instance-controller-param-change-fallback-to-deploy/01-assert.yaml @@ -6,6 +6,5 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: deploy + deploy: status: COMPLETE - - name: upgrade diff --git a/test/integration/instance-controller-param-change-no-trigger/00-assert.yaml b/test/integration/instance-controller-param-change-no-trigger/00-assert.yaml index 481c033ef..1d776711a 100644 --- a/test/integration/instance-controller-param-change-no-trigger/00-assert.yaml +++ b/test/integration/instance-controller-param-change-no-trigger/00-assert.yaml @@ -6,6 +6,5 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: deploy + deploy: status: COMPLETE - - name: update diff --git a/test/integration/instance-controller-param-change-no-trigger/01-assert.yaml b/test/integration/instance-controller-param-change-no-trigger/01-assert.yaml index 52d63dd24..b8ee797d8 100644 --- a/test/integration/instance-controller-param-change-no-trigger/01-assert.yaml +++ b/test/integration/instance-controller-param-change-no-trigger/01-assert.yaml @@ -6,6 +6,5 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: update + update: status: COMPLETE - - name: deploy diff --git a/test/integration/instance-controller-upgrade-fallback-to-deploy/00-assert.yaml b/test/integration/instance-controller-upgrade-fallback-to-deploy/00-assert.yaml index 953c2bf0c..75531516a 100644 --- a/test/integration/instance-controller-upgrade-fallback-to-deploy/00-assert.yaml +++ b/test/integration/instance-controller-upgrade-fallback-to-deploy/00-assert.yaml @@ -6,5 +6,5 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: deploy + deploy: status: COMPLETE diff --git a/test/integration/instance-controller-upgrade-fallback-to-deploy/01-assert.yaml b/test/integration/instance-controller-upgrade-fallback-to-deploy/01-assert.yaml index 953c2bf0c..75531516a 100644 --- a/test/integration/instance-controller-upgrade-fallback-to-deploy/01-assert.yaml +++ b/test/integration/instance-controller-upgrade-fallback-to-deploy/01-assert.yaml @@ -6,5 +6,5 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: deploy + deploy: status: COMPLETE diff --git a/test/integration/instance-controller-upgrade-fallback-to-update/00-assert.yaml b/test/integration/instance-controller-upgrade-fallback-to-update/00-assert.yaml index 6848a3a81..245b3873b 100644 --- a/test/integration/instance-controller-upgrade-fallback-to-update/00-assert.yaml +++ b/test/integration/instance-controller-upgrade-fallback-to-update/00-assert.yaml @@ -6,7 +6,5 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: deploy + deploy: status: COMPLETE - - name: upgrade - - name: update diff --git a/test/integration/instance-controller-upgrade-fallback-to-update/01-assert.yaml b/test/integration/instance-controller-upgrade-fallback-to-update/01-assert.yaml index c4e59cae4..a7583eb8f 100644 --- a/test/integration/instance-controller-upgrade-fallback-to-update/01-assert.yaml +++ b/test/integration/instance-controller-upgrade-fallback-to-update/01-assert.yaml @@ -6,5 +6,5 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: update + update: status: COMPLETE diff --git a/test/integration/instance-controller-upgrade/00-assert.yaml b/test/integration/instance-controller-upgrade/00-assert.yaml index 7be596645..8f91d4a50 100644 --- a/test/integration/instance-controller-upgrade/00-assert.yaml +++ b/test/integration/instance-controller-upgrade/00-assert.yaml @@ -6,6 +6,5 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: deploy + deploy: status: COMPLETE - - name: upgrade diff --git a/test/integration/instance-controller-upgrade/01-assert.yaml b/test/integration/instance-controller-upgrade/01-assert.yaml index a9f310716..5066a4d69 100644 --- a/test/integration/instance-controller-upgrade/01-assert.yaml +++ b/test/integration/instance-controller-upgrade/01-assert.yaml @@ -6,6 +6,5 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: deploy - - name: upgrade + upgrade: status: COMPLETE \ No newline at end of file diff --git a/test/integration/racy-reconcile/01-assert.yaml b/test/integration/racy-reconcile/01-assert.yaml index b56af8ec3..7130b9416 100644 --- a/test/integration/racy-reconcile/01-assert.yaml +++ b/test/integration/racy-reconcile/01-assert.yaml @@ -6,5 +6,5 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: deploy + deploy: status: COMPLETE diff --git a/test/integration/rendering-error-plan-failed/00-assert.yaml b/test/integration/rendering-error-plan-failed/00-assert.yaml index 0d6a874d6..52007ba4c 100644 --- a/test/integration/rendering-error-plan-failed/00-assert.yaml +++ b/test/integration/rendering-error-plan-failed/00-assert.yaml @@ -6,7 +6,7 @@ status: aggregatedStatus: status: FATAL_ERROR planStatus: - - name: deploy + deploy: status: FATAL_ERROR phases: - name: invalid-phase diff --git a/test/integration/update-parameters/00-assert.yaml b/test/integration/update-parameters/00-assert.yaml index b417df97e..4a42920e8 100644 --- a/test/integration/update-parameters/00-assert.yaml +++ b/test/integration/update-parameters/00-assert.yaml @@ -15,9 +15,7 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: scale - - name: update - - name: deploy + deploy: status: COMPLETE phases: - name: deploy diff --git a/test/integration/update-parameters/01-assert.yaml b/test/integration/update-parameters/01-assert.yaml index a067a49e4..15bd8add8 100644 --- a/test/integration/update-parameters/01-assert.yaml +++ b/test/integration/update-parameters/01-assert.yaml @@ -15,9 +15,7 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: deploy - - name: update - - name: scale + scale: status: COMPLETE phases: - name: scale diff --git a/test/integration/update-parameters/02-assert.yaml b/test/integration/update-parameters/02-assert.yaml index d6af1cb2f..4d4bff2d4 100644 --- a/test/integration/update-parameters/02-assert.yaml +++ b/test/integration/update-parameters/02-assert.yaml @@ -14,9 +14,7 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: scale - - name: deploy - - name: update + update: status: COMPLETE phases: - name: update diff --git a/test/integration/upgrade-command/01-assert.yaml b/test/integration/upgrade-command/01-assert.yaml index 0903ef184..479b25b13 100644 --- a/test/integration/upgrade-command/01-assert.yaml +++ b/test/integration/upgrade-command/01-assert.yaml @@ -11,5 +11,5 @@ status: aggregatedStatus: status: COMPLETE planStatus: - - name: upgrade + upgrade: status: COMPLETE \ No newline at end of file From c3fc262448bf55c1eedc0e24e7619e37dce914b1 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Wed, 25 Sep 2019 13:39:19 +0200 Subject: [PATCH 15/29] Fix when the plan snapshot has to be stored --- pkg/apis/kudo/v1alpha1/instance_types.go | 10 ++++------ pkg/controller/instance/instance_controller.go | 7 +++++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index 3e077f1dc..dd2b90483 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -183,12 +183,10 @@ func (i *Instance) StartPlanExecution(planName string, ov *OperatorVersion) erro return fmt.Errorf("asked to execute a plan %s but no such plan found in instance %s/%s", planName, i.Namespace, i.Name) } - // save snapshot prior to execution for plans requiring snapshot - if planName == "deploy" || planName == "update" || planName == "upgrade" { - err := i.SaveSnapshot() - if err != nil { - return err - } + // TODO in the future when we again support manual plan execution, snapshot should be saved only non non-manually executed plans + err := i.SaveSnapshot() + if err != nil { + return err } return nil diff --git a/pkg/controller/instance/instance_controller.go b/pkg/controller/instance/instance_controller.go index 2d1906c3b..983830d19 100644 --- a/pkg/controller/instance/instance_controller.go +++ b/pkg/controller/instance/instance_controller.go @@ -34,6 +34,7 @@ import ( kudov1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -49,7 +50,7 @@ type Reconciler struct { // SetupWithManager registers this reconciler with the controller manager func (r *Reconciler) SetupWithManager( mgr ctrl.Manager) error { - ovEventHandler := handler.ToRequestsFunc( + addOvRelatedInstancesToReconcile := handler.ToRequestsFunc( func(a handler.MapObject) []reconcile.Request { requests := make([]reconcile.Request, 0) instances := &kudov1alpha1.InstanceList{} @@ -82,9 +83,11 @@ func (r *Reconciler) SetupWithManager( For(&kudov1alpha1.Instance{}). Owns(&kudov1alpha1.Instance{}). Owns(&appsv1.Deployment{}). + Owns(&corev1.Service{}). Owns(&batchv1.Job{}). Owns(&appsv1.StatefulSet{}). - Watches(&source.Kind{Type: &kudov1alpha1.OperatorVersion{}}, &handler.EnqueueRequestsFromMapFunc{ToRequests: ovEventHandler}).Complete(r) + Watches(&source.Kind{Type: &kudov1alpha1.OperatorVersion{}}, &handler.EnqueueRequestsFromMapFunc{ToRequests: addOvRelatedInstancesToReconcile}). + Complete(r) } // Reconcile ... From 7f4712e4201777649218541b0702363e2cad4c7e Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Wed, 25 Sep 2019 16:29:29 +0200 Subject: [PATCH 16/29] Make the last test pass --- pkg/apis/kudo/v1alpha1/instance_types.go | 42 ++++++++++++++++--- .../upgrade-command/01-assert.yaml | 8 ++-- .../upgrade-command/01-upgrade.yaml | 2 +- .../first-operator/operator.yaml | 2 +- .../first-operator/params.yaml | 6 +-- .../first-operator/templates/deployment.yaml | 19 --------- .../first-operator/templates/pod.yaml | 9 ++++ .../second-operator/operator.yaml | 2 +- .../second-operator/params.yaml | 6 +-- .../second-operator/templates/deployment.yaml | 19 --------- .../second-operator/templates/pod.yaml | 9 ++++ 11 files changed, 67 insertions(+), 57 deletions(-) delete mode 100644 test/integration/upgrade-command/first-operator/templates/deployment.yaml create mode 100644 test/integration/upgrade-command/first-operator/templates/pod.yaml delete mode 100644 test/integration/upgrade-command/second-operator/templates/deployment.yaml create mode 100644 test/integration/upgrade-command/second-operator/templates/pod.yaml diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index dd2b90483..0db96c49a 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -18,11 +18,10 @@ package v1alpha1 import ( "encoding/json" "fmt" + "github.com/kudobuilder/kudo/pkg/util/kudo" "log" "reflect" - "github.com/kudobuilder/kudo/pkg/util/kudo" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -122,8 +121,11 @@ func (i *Instance) NoPlanEverExecuted() bool { // EnsurePlanStatusInitialized initializes plan status for all plans this instance supports // it does not trigger run of any plan +// it either initializes everything for a fresh instance without any status or tries to adjust status after OV was updated func (i *Instance) EnsurePlanStatusInitialized(ov *OperatorVersion) { - i.Status.PlanStatus = make(map[string]PlanStatus) + if i.Status.PlanStatus == nil { + i.Status.PlanStatus = make(map[string]PlanStatus) + } for planName, plan := range ov.Spec.Plans { planStatus := &PlanStatus{ @@ -131,17 +133,40 @@ func (i *Instance) EnsurePlanStatusInitialized(ov *OperatorVersion) { Status: ExecutionNeverRun, Phases: make([]PhaseStatus, 0), } + + existingPlanStatus, planExists := i.Status.PlanStatus[planName] + if planExists { + planStatus.Status = existingPlanStatus.Status + } for _, phase := range plan.Phases { phaseStatus := &PhaseStatus{ Name: phase.Name, Status: ExecutionNeverRun, Steps: make([]StepStatus, 0), } + existingPhaseStatus, phaseExists := PhaseStatus{}, false + if planExists { + for _, oldPhase := range existingPlanStatus.Phases { + if phase.Name == oldPhase.Name { + existingPhaseStatus = oldPhase + phaseExists = true + phaseStatus.Status = existingPhaseStatus.Status + } + } + } for _, step := range phase.Steps { - phaseStatus.Steps = append(phaseStatus.Steps, StepStatus{ + stepStatus := StepStatus{ Name: step.Name, Status: ExecutionNeverRun, - }) + } + if phaseExists { + for _, oldStep := range existingPhaseStatus.Steps { + if step.Name == oldStep.Name { + stepStatus.Status = oldStep.Status + } + } + } + phaseStatus.Steps = append(phaseStatus.Steps, stepStatus) } planStatus.Phases = append(planStatus.Phases, *phaseStatus) } @@ -151,7 +176,7 @@ func (i *Instance) EnsurePlanStatusInitialized(ov *OperatorVersion) { // StartPlanExecution mark plan as to be executed func (i *Instance) StartPlanExecution(planName string, ov *OperatorVersion) error { - if i.NoPlanEverExecuted() { + if i.NoPlanEverExecuted() || isUpgradePlan(planName) { i.EnsurePlanStatusInitialized(ov) } @@ -192,6 +217,11 @@ func (i *Instance) StartPlanExecution(planName string, ov *OperatorVersion) erro return nil } +// isUpgradePlan returns true if this could be an upgrade plan - this is just an approximation because deploy plan can be used for both +func isUpgradePlan(planName string) bool { + return planName == "deploy" || planName == "upgrade" +} + // UpdateInstanceStatus updates `Status.PlanStatus` and `Status.AggregatedStatus` property based on the given plan func (i *Instance) UpdateInstanceStatus(planStatus *PlanStatus) { for k, v := range i.Status.PlanStatus { diff --git a/test/integration/upgrade-command/01-assert.yaml b/test/integration/upgrade-command/01-assert.yaml index 479b25b13..7144149db 100644 --- a/test/integration/upgrade-command/01-assert.yaml +++ b/test/integration/upgrade-command/01-assert.yaml @@ -6,10 +6,10 @@ spec: operatorVersion: name: upgrade-operator-0.2.0 parameters: - replicas: "4" + version: 1.7.10 status: aggregatedStatus: status: COMPLETE - planStatus: - upgrade: - status: COMPLETE \ No newline at end of file + planStatus: + upgrade: + status: COMPLETE \ No newline at end of file diff --git a/test/integration/upgrade-command/01-upgrade.yaml b/test/integration/upgrade-command/01-upgrade.yaml index 7c5ddb8a6..57c8c3c34 100644 --- a/test/integration/upgrade-command/01-upgrade.yaml +++ b/test/integration/upgrade-command/01-upgrade.yaml @@ -1,4 +1,4 @@ apiVersion: kudo.dev/v1alpha1 kind: TestStep kubectl: - - kudo upgrade --instance upgrade-instance ./second-operator -p replicas=4 + - kudo upgrade --instance upgrade-instance ./second-operator -p version=1.7.10 diff --git a/test/integration/upgrade-command/first-operator/operator.yaml b/test/integration/upgrade-command/first-operator/operator.yaml index 57394852f..5be9903b8 100644 --- a/test/integration/upgrade-command/first-operator/operator.yaml +++ b/test/integration/upgrade-command/first-operator/operator.yaml @@ -8,7 +8,7 @@ url: https://kudo.dev tasks: app: resources: - - deployment.yaml + - pod.yaml plans: deploy: strategy: serial diff --git a/test/integration/upgrade-command/first-operator/params.yaml b/test/integration/upgrade-command/first-operator/params.yaml index 55d06c632..d5a286d71 100644 --- a/test/integration/upgrade-command/first-operator/params.yaml +++ b/test/integration/upgrade-command/first-operator/params.yaml @@ -1,3 +1,3 @@ -replicas: - description: Number of replicas that should be run as part of the deployment - default: 2 +version: + description: Version of image + default: 1.7.9 diff --git a/test/integration/upgrade-command/first-operator/templates/deployment.yaml b/test/integration/upgrade-command/first-operator/templates/deployment.yaml deleted file mode 100644 index 73b0ad025..000000000 --- a/test/integration/upgrade-command/first-operator/templates/deployment.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - selector: - matchLabels: - app: nginx - replicas: {{ .Params.replicas }} # tells deployment to run 2 pods matching the template - template: - metadata: - labels: - app: nginx - spec: - containers: - - name: nginx - image: nginx:1.7.9 - ports: - - containerPort: 80 \ No newline at end of file diff --git a/test/integration/upgrade-command/first-operator/templates/pod.yaml b/test/integration/upgrade-command/first-operator/templates/pod.yaml new file mode 100644 index 000000000..52d7dcb44 --- /dev/null +++ b/test/integration/upgrade-command/first-operator/templates/pod.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test6 +spec: + restartPolicy: Never + containers: + - name: nginx + image: nginx:{{ .Params.version }} \ No newline at end of file diff --git a/test/integration/upgrade-command/second-operator/operator.yaml b/test/integration/upgrade-command/second-operator/operator.yaml index 8fd89f285..081526ad9 100644 --- a/test/integration/upgrade-command/second-operator/operator.yaml +++ b/test/integration/upgrade-command/second-operator/operator.yaml @@ -8,7 +8,7 @@ url: https://kudo.dev tasks: app: resources: - - deployment.yaml + - pod.yaml plans: deploy: strategy: serial diff --git a/test/integration/upgrade-command/second-operator/params.yaml b/test/integration/upgrade-command/second-operator/params.yaml index 55d06c632..d5a286d71 100644 --- a/test/integration/upgrade-command/second-operator/params.yaml +++ b/test/integration/upgrade-command/second-operator/params.yaml @@ -1,3 +1,3 @@ -replicas: - description: Number of replicas that should be run as part of the deployment - default: 2 +version: + description: Version of image + default: 1.7.9 diff --git a/test/integration/upgrade-command/second-operator/templates/deployment.yaml b/test/integration/upgrade-command/second-operator/templates/deployment.yaml deleted file mode 100644 index f0583593a..000000000 --- a/test/integration/upgrade-command/second-operator/templates/deployment.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - selector: - matchLabels: - app: nginx - replicas: {{ .Params.replicas }} # tells deployment to run 2 pods matching the template - template: - metadata: - labels: - app: nginx - spec: - containers: - - name: nginx - image: nginx:1.8.1 - ports: - - containerPort: 80 \ No newline at end of file diff --git a/test/integration/upgrade-command/second-operator/templates/pod.yaml b/test/integration/upgrade-command/second-operator/templates/pod.yaml new file mode 100644 index 000000000..52d7dcb44 --- /dev/null +++ b/test/integration/upgrade-command/second-operator/templates/pod.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test6 +spec: + restartPolicy: Never + containers: + - name: nginx + image: nginx:{{ .Params.version }} \ No newline at end of file From fb739b0b103428c028d699449b286ec938a9c031 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Wed, 25 Sep 2019 17:03:26 +0200 Subject: [PATCH 17/29] Format --- pkg/apis/kudo/v1alpha1/instance_types.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index 0db96c49a..9db1c98e7 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -18,10 +18,11 @@ package v1alpha1 import ( "encoding/json" "fmt" - "github.com/kudobuilder/kudo/pkg/util/kudo" "log" "reflect" + "github.com/kudobuilder/kudo/pkg/util/kudo" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) From 0e81033773fb681332320e7899b0f49307c25d22 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Thu, 26 Sep 2019 15:33:25 +0200 Subject: [PATCH 18/29] Add docs and missing events --- pkg/apis/kudo/v1alpha1/instance_types.go | 46 ++++++++++++++++--- .../instance/instance_controller.go | 39 +++++++++++++++- .../instance/plan_execution_engine.go | 18 ++++---- .../instance/plan_execution_engine_test.go | 4 +- 4 files changed, 87 insertions(+), 20 deletions(-) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index 9db1c98e7..bda68225e 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -49,6 +49,28 @@ type AggregatedStatus struct { } // PlanStatus is representing status of a plan +// +// These are valid states and trainsitions +// +// +----------------+ +// | Never executed | +// +-------+--------+ +// | +// v +//+-------------+ +-------+--------+ +//| Error |<------>| Pending | +//+------+------+ +-------+--------+ +// ^ | +// | v +// | +-------+--------+ +// +-------------->| In progress | +// | +-------+--------+ +// | | +// v v +//+------+------+ +-------+--------+ +//| Fatal error | | Complete | +//+-------------+ +----------------+ +// type PlanStatus struct { Name string `json:"name,omitempty"` Status ExecutionStatus `json:"status,omitempty"` @@ -81,8 +103,8 @@ const ExecutionPending ExecutionStatus = "PENDING" // ExecutionComplete deployed and healthy. const ExecutionComplete ExecutionStatus = "COMPLETE" -// ExecutionError there was an error deploying the application. -const ExecutionError ExecutionStatus = "ERROR" +// ErrorStatus there was an error deploying the application. +const ErrorStatus ExecutionStatus = "ERROR" // ExecutionFatalError there was an error deploying the application. const ExecutionFatalError ExecutionStatus = "FATAL_ERROR" @@ -97,7 +119,7 @@ func (s ExecutionStatus) IsTerminal() bool { // IsRunning returns true if the plan is currently being executed func (s ExecutionStatus) IsRunning() bool { - return s == ExecutionInProgress || s == ExecutionPending || s == ExecutionError + return s == ExecutionInProgress || s == ExecutionPending || s == ErrorStatus } // GetPlanInProgress returns plan status of currently active plan or nil if no plan is running @@ -123,6 +145,7 @@ func (i *Instance) NoPlanEverExecuted() bool { // EnsurePlanStatusInitialized initializes plan status for all plans this instance supports // it does not trigger run of any plan // it either initializes everything for a fresh instance without any status or tries to adjust status after OV was updated +// TODO rev: test me func (i *Instance) EnsurePlanStatusInitialized(ov *OperatorVersion) { if i.Status.PlanStatus == nil { i.Status.PlanStatus = make(map[string]PlanStatus) @@ -206,7 +229,7 @@ func (i *Instance) StartPlanExecution(planName string, ov *OperatorVersion) erro } } if notFound { - return fmt.Errorf("asked to execute a plan %s but no such plan found in instance %s/%s", planName, i.Namespace, i.Name) + return &InstanceError{fmt.Errorf("asked to execute a plan %s but no such plan found in instance %s/%s", planName, i.Namespace, i.Name), kudo.String("PlanNotFound")} } // TODO in the future when we again support manual plan execution, snapshot should be saved only non non-manually executed plans @@ -293,12 +316,12 @@ func (i *Instance) GetPlanToBeExecuted(ov *OperatorVersion) (*string, error) { if err != nil { return nil, err } - if instanceSnapshot.OperatorVersion.Name != i.Spec.OperatorVersion.Name || instanceSnapshot.OperatorVersion.Namespace != i.Spec.OperatorVersion.Namespace { + if instanceSnapshot.OperatorVersion.Name != i.Spec.OperatorVersion.Name { // this instance was upgraded to newer version log.Printf("Instance: instance %s/%s was upgraded from %s to %s operatorversion", i.Namespace, i.Name, instanceSnapshot.OperatorVersion.Name, i.Spec.OperatorVersion.Name) plan := selectPlan([]string{"upgrade", "update", "deploy"}, ov) if plan == nil { - return nil, fmt.Errorf("supposed to execute plan because instance %s/%s was upgraded but none of the deploy, upgrade, update plans found in linked operatorVersion", i.Namespace, i.Name) + return nil, &InstanceError{fmt.Errorf("supposed to execute plan because instance %s/%s was upgraded but none of the deploy, upgrade, update plans found in linked operatorVersion", i.Namespace, i.Name), kudo.String("PlanNotFound")} } return plan, nil } @@ -309,7 +332,7 @@ func (i *Instance) GetPlanToBeExecuted(ov *OperatorVersion) (*string, error) { paramDefinitions := getParamDefinitions(paramDiff, ov) plan := planNameFromParameters(paramDefinitions, ov) if plan == nil { - return nil, fmt.Errorf("supposed to execute plan because instance %s/%s was updatet but none of the deploy, update plans found in linked operatorVersion", i.Namespace, i.Name) + return nil, &InstanceError{fmt.Errorf("supposed to execute plan because instance %s/%s was updatet but none of the deploy, update plans found in linked operatorVersion", i.Namespace, i.Name), kudo.String("PlanNotFound")} } return plan, nil } @@ -394,3 +417,12 @@ type InstanceList struct { func init() { SchemeBuilder.Register(&Instance{}, &InstanceList{}) } + +type InstanceError struct { + err error + EventName *string // nil if no warn event should be created +} + +func (e *InstanceError) Error() string { + return fmt.Sprintf("Error during execution: %v", e.err) +} diff --git a/pkg/controller/instance/instance_controller.go b/pkg/controller/instance/instance_controller.go index 983830d19..1d42deeac 100644 --- a/pkg/controller/instance/instance_controller.go +++ b/pkg/controller/instance/instance_controller.go @@ -90,7 +90,30 @@ func (r *Reconciler) SetupWithManager( Complete(r) } -// Reconcile ... +// Reconcile is the main controller method that gets called every time something about the instance changes +// +// +-------------------------------+ +// | Query state of Instance | +// | and OperatorVersion | +// +-------------------------------+ +// | +// v +// +-------------------------------+ +// | Start new plan if required | +// | and none is running | +// +-------------------------------+ +// | +// v +// +-------------------------------+ +// | If there is plan in progress, | +// | proceed with the execution | +// +-------------------------------+ +// | +// v +// +-------------------------------+ +// | Update instance with new | +// | state of the execution | +// +-------------------------------+ // // Automatically generate RBAC rules to allow the Controller to read and write Deployments // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete @@ -124,6 +147,7 @@ func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { if err != nil { return reconcile.Result{}, r.handleError(err, instance) } + r.Recorder.Event(instance, "Normal", "PlanStarted", fmt.Sprintf("Execution of plan %s started", kudo.StringValue(planToBeExecuted))) } // ---------- 3. If there's currently active plan, continue with the execution ---------- @@ -159,6 +183,10 @@ func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { return reconcile.Result{}, err } + if instance.Status.AggregatedStatus.Status.IsTerminal() { + r.Recorder.Event(instance, "Normal", "PlanFinished", fmt.Sprintf("Execution of plan %s finished with status %s", activePlanStatus.Name, instance.Status.AggregatedStatus.Status)) + } + return reconcile.Result{}, nil } @@ -213,6 +241,13 @@ func (r *Reconciler) handleError(err error, instance *kudov1alpha1.Instance) err return nil // not retrying fatal error } } + + // for code being processed on instance, we need to handle these errors as well + if iError, ok := err.(*kudov1alpha1.InstanceError); ok { + if iError.EventName != nil { + r.Recorder.Event(instance, "Warning", kudo.StringValue(iError.EventName), err.Error()) + } + } return err } @@ -305,7 +340,7 @@ type executionError struct { eventName *string // nil if no warn even should be created } -func (e executionError) Error() string { +func (e *executionError) Error() string { if e.fatal { return fmt.Sprintf("Fatal error: %v", e.err) } diff --git a/pkg/controller/instance/plan_execution_engine.go b/pkg/controller/instance/plan_execution_engine.go index 61b7544b5..a8db1a3cf 100644 --- a/pkg/controller/instance/plan_execution_engine.go +++ b/pkg/controller/instance/plan_execution_engine.go @@ -56,7 +56,7 @@ type executionMetadata struct { // the next step could consist of actually executing multiple steps of the plan or just one depending on the execution strategy of the phase (serial/parallel) // result of running this function is new state of the execution that is returned to the caller (it can either be completed, or still in progress or errored) // in case of error, error is returned along with the state as well (so that it's possible to report which step caused the error) -// in case of error, method returns executionError which has property to indicate unrecoverable error meaning if there is no point in retrying that execution +// in case of error, method returns ErrorStatus which has property to indicate unrecoverable error meaning if there is no point in retrying that execution func executePlan(plan *activePlan, metadata *executionMetadata, c client.Client, renderer kubernetesObjectEnhancer) (*v1alpha1.PlanStatus, error) { if plan.Status.IsTerminal() { log.Printf("PlanExecution: Plan %s for instance %s is terminal, nothing to do", plan.Name, metadata.instanceName) @@ -73,7 +73,7 @@ func executePlan(plan *activePlan, metadata *executionMetadata, c client.Client, if errors.As(err, &exErr) { newState.Status = v1alpha1.ExecutionFatalError } else { - newState.Status = v1alpha1.ExecutionError + newState.Status = v1alpha1.ErrorStatus } return newState, err } @@ -100,8 +100,8 @@ func executePlan(plan *activePlan, metadata *executionMetadata, c client.Client, log.Printf("PlanExecution: Executing step %s on plan %s and instance %s - it's in %s state", st.Name, plan.Name, metadata.instanceName, currentStepState.Status) err := executeStep(st, currentStepState, resources, c) if err != nil { - currentPhaseState.Status = v1alpha1.ExecutionError - currentStepState.Status = v1alpha1.ExecutionError + currentPhaseState.Status = v1alpha1.ErrorStatus + currentStepState.Status = v1alpha1.ErrorStatus return newState, err } @@ -297,16 +297,16 @@ func prepareKubeResources(plan *activePlan, meta *executionMetadata, renderer ku }, meta.resourcesOwner) if err != nil { - phaseState.Status = v1alpha1.ExecutionError - stepState.Status = v1alpha1.ExecutionError + phaseState.Status = v1alpha1.ErrorStatus + stepState.Status = v1alpha1.ErrorStatus log.Printf("Error creating Kubernetes objects from step %v in phase %v of plan %v: %v", step.Name, phase.Name, meta.planExecutionID, err) return nil, &executionError{err, false, nil} } resources = append(resources, resourcesWithConventions...) } else { - phaseState.Status = v1alpha1.ExecutionError - stepState.Status = v1alpha1.ExecutionError + phaseState.Status = v1alpha1.ErrorStatus + stepState.Status = v1alpha1.ErrorStatus err := fmt.Errorf("Error finding task named %s for operator version %s", taskSpec, meta.operatorVersionName) log.Print(err) @@ -344,5 +344,5 @@ func isFinished(state v1alpha1.ExecutionStatus) bool { } func isInProgress(state v1alpha1.ExecutionStatus) bool { - return state == v1alpha1.ExecutionInProgress || state == v1alpha1.ExecutionPending || state == v1alpha1.ExecutionError + return state == v1alpha1.ExecutionInProgress || state == v1alpha1.ExecutionPending || state == v1alpha1.ErrorStatus } diff --git a/pkg/controller/instance/plan_execution_engine_test.go b/pkg/controller/instance/plan_execution_engine_test.go index 79267427e..bcad445b9 100644 --- a/pkg/controller/instance/plan_execution_engine_test.go +++ b/pkg/controller/instance/plan_execution_engine_test.go @@ -86,9 +86,9 @@ func TestExecutePlan(t *testing.T) { {"plan in errored state will be retried and completed when no error happens", &activePlan{ Name: "test", PlanStatus: &v1alpha1.PlanStatus{ - Status: v1alpha1.ExecutionError, + Status: v1alpha1.ErrorStatus, Name: "test", - Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ExecutionError, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ExecutionError, Name: "step"}}}}, + Phases: []v1alpha1.PhaseStatus{{Name: "phase", Status: v1alpha1.ErrorStatus, Steps: []v1alpha1.StepStatus{{Status: v1alpha1.ErrorStatus, Name: "step"}}}}, }, Spec: &v1alpha1.Plan{ Strategy: "serial", From a31577083756240221310aeb98f64cdb76d628fe Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Thu, 26 Sep 2019 15:33:54 +0200 Subject: [PATCH 19/29] Extra comment --- pkg/apis/kudo/v1alpha1/instance_types.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index bda68225e..f5822c142 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -145,7 +145,6 @@ func (i *Instance) NoPlanEverExecuted() bool { // EnsurePlanStatusInitialized initializes plan status for all plans this instance supports // it does not trigger run of any plan // it either initializes everything for a fresh instance without any status or tries to adjust status after OV was updated -// TODO rev: test me func (i *Instance) EnsurePlanStatusInitialized(ov *OperatorVersion) { if i.Status.PlanStatus == nil { i.Status.PlanStatus = make(map[string]PlanStatus) From 7c48dca71b31d5f7959580ae534908bd9fac0613 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Thu, 26 Sep 2019 19:39:54 +0200 Subject: [PATCH 20/29] Fix format --- pkg/apis/kudo/v1alpha1/instance_types.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index f5822c142..9181db749 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -417,6 +417,7 @@ func init() { SchemeBuilder.Register(&Instance{}, &InstanceList{}) } +// InstanceError indicates error on that can also emit a kubernetes warn event type InstanceError struct { err error EventName *string // nil if no warn event should be created From 04b50ab3751770842ffcde7cd789f91dd086af2b Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Fri, 27 Sep 2019 08:45:22 +0200 Subject: [PATCH 21/29] Update pkg/apis/kudo/v1alpha1/instance_types.go Co-Authored-By: Ken Sipe --- pkg/apis/kudo/v1alpha1/instance_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index 9181db749..7aab80522 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -231,7 +231,7 @@ func (i *Instance) StartPlanExecution(planName string, ov *OperatorVersion) erro return &InstanceError{fmt.Errorf("asked to execute a plan %s but no such plan found in instance %s/%s", planName, i.Namespace, i.Name), kudo.String("PlanNotFound")} } - // TODO in the future when we again support manual plan execution, snapshot should be saved only non non-manually executed plans + // TODO in the future when we again support manual plan execution, snapshot should be saved only manually executed plans err := i.SaveSnapshot() if err != nil { return err From 5a089bd4e70673248cf677fba8bf4c517eb22d7e Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Fri, 27 Sep 2019 08:47:35 +0200 Subject: [PATCH 22/29] Some PR feedback --- pkg/apis/kudo/v1alpha1/instance_types.go | 12 ++++++------ pkg/controller/instance/instance_controller.go | 15 ++++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index 9181db749..69fdac9bd 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -211,10 +211,10 @@ func (i *Instance) StartPlanExecution(planName string, ov *OperatorVersion) erro notFound = false planStatus := i.Status.PlanStatus[n1] planStatus.Status = ExecutionPending - for i2, p := range v.Phases { - planStatus.Phases[i2].Status = ExecutionPending - for i3 := range p.Steps { - i.Status.PlanStatus[n1].Phases[i2].Steps[i3].Status = ExecutionPending + for j, p := range v.Phases { + planStatus.Phases[j].Status = ExecutionPending + for k := range p.Steps { + i.Status.PlanStatus[n1].Phases[j].Steps[k].Status = ExecutionPending } } @@ -396,8 +396,8 @@ type Instance struct { Status InstanceStatus `json:"status,omitempty"` } -// GetOperatorVersionNamespace returns the namespace of the OperatorVersion that the Instance references. -func (i *Instance) GetOperatorVersionNamespace() string { +// OperatorVersionNamespace returns the namespace of the OperatorVersion that the Instance references. +func (i *Instance) OperatorVersionNamespace() string { if i.Spec.OperatorVersion.Namespace == "" { return i.ObjectMeta.Namespace } diff --git a/pkg/controller/instance/instance_controller.go b/pkg/controller/instance/instance_controller.go index 1d42deeac..7f84660ef 100644 --- a/pkg/controller/instance/instance_controller.go +++ b/pkg/controller/instance/instance_controller.go @@ -51,23 +51,23 @@ type Reconciler struct { func (r *Reconciler) SetupWithManager( mgr ctrl.Manager) error { addOvRelatedInstancesToReconcile := handler.ToRequestsFunc( - func(a handler.MapObject) []reconcile.Request { + func(obj handler.MapObject) []reconcile.Request { requests := make([]reconcile.Request, 0) instances := &kudov1alpha1.InstanceList{} // we are listing all instances here, which could come with some performance penalty - // a possible optimization is to introduce filtering based on operatorversion (or operator) + // obj possible optimization is to introduce filtering based on operatorversion (or operator) err := mgr.GetClient().List( context.TODO(), instances, ) if err != nil { - log.Printf("InstanceController: Error fetching instances list for operator %v: %v", a.Meta.GetName(), err) + log.Printf("InstanceController: Error fetching instances list for operator %v: %v", obj.Meta.GetName(), err) return nil } for _, instance := range instances.Items { // Sanity check - lets make sure that this instance references the operatorVersion - if instance.Spec.OperatorVersion.Name == a.Meta.GetName() && - instance.GetOperatorVersionNamespace() == a.Meta.GetNamespace() { + if instance.Spec.OperatorVersion.Name == obj.Meta.GetName() && + instance.OperatorVersionNamespace() == obj.Meta.GetNamespace() { requests = append(requests, reconcile.Request{ NamespacedName: types.NamespacedName{ Name: instance.Name, @@ -125,6 +125,7 @@ func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { instance, err := r.getInstance(request) if err != nil { if apierrors.IsNotFound(err) { // not retrying if instance not found, probably someone manually removed it? + log.Printf("Instance %s/%s not found, not retrying reconcile since this error is usually not recoverable (without manual intervention).", instance.Namespace, instance.Name) return reconcile.Result{}, nil } return reconcile.Result{}, err @@ -132,7 +133,7 @@ func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { ov, err := r.getOperatorVersion(instance) if err != nil { - return reconcile.Result{}, err // OV not found has to be retried because it can really have been created after I + return reconcile.Result{}, err // OV not found has to be retried because it can really have been created after Instance } // ---------- 2. First check if we should start execution of new plan ---------- @@ -273,7 +274,7 @@ func (r *Reconciler) getOperatorVersion(instance *kudov1alpha1.Instance) (ov *ku err = r.Get(context.TODO(), types.NamespacedName{ Name: instance.Spec.OperatorVersion.Name, - Namespace: instance.GetOperatorVersionNamespace(), + Namespace: instance.OperatorVersionNamespace(), }, ov) if err != nil { From 3d0aa1bf6881a99f30d183d752a2e01e9ad7cf64 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Fri, 27 Sep 2019 14:47:46 +0200 Subject: [PATCH 23/29] Remaining PR comments --- pkg/apis/kudo/v1alpha1/instance_types.go | 12 ++++++------ pkg/controller/instance/instance_controller.go | 6 ++++-- pkg/controller/instance/plan_execution_engine.go | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index 69fdac9bd..3e16b82d8 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -205,20 +205,20 @@ func (i *Instance) StartPlanExecution(planName string, ov *OperatorVersion) erro // update status of the instance to reflect the newly starting plan notFound := true - for n1, v := range i.Status.PlanStatus { + for planIndex, v := range i.Status.PlanStatus { if v.Name == planName { // update plan status notFound = false - planStatus := i.Status.PlanStatus[n1] + planStatus := i.Status.PlanStatus[planIndex] planStatus.Status = ExecutionPending for j, p := range v.Phases { planStatus.Phases[j].Status = ExecutionPending for k := range p.Steps { - i.Status.PlanStatus[n1].Phases[j].Steps[k].Status = ExecutionPending + i.Status.PlanStatus[planIndex].Phases[j].Steps[k].Status = ExecutionPending } } - i.Status.PlanStatus[n1] = planStatus // we cannot modify item in map, we need to reassign here + i.Status.PlanStatus[planIndex] = planStatus // we cannot modify item in map, we need to reassign here // update activePlan and instance status i.Status.AggregatedStatus.Status = ExecutionPending @@ -274,7 +274,7 @@ func (i *Instance) SaveSnapshot() error { return nil } -func (i *Instance) getSnapshotedSpec() (*InstanceSpec, error) { +func (i *Instance) snapshotSpec() (*InstanceSpec, error) { if i.Annotations != nil { snapshot, ok := i.Annotations[snapshotAnnotation] if ok { @@ -311,7 +311,7 @@ func (i *Instance) GetPlanToBeExecuted(ov *OperatorVersion) (*string, error) { } // did the instance change so that we need to run deploy/upgrade/update plan? - instanceSnapshot, err := i.getSnapshotedSpec() + instanceSnapshot, err := i.snapshotSpec() if err != nil { return nil, err } diff --git a/pkg/controller/instance/instance_controller.go b/pkg/controller/instance/instance_controller.go index 7f84660ef..f12ae1e1e 100644 --- a/pkg/controller/instance/instance_controller.go +++ b/pkg/controller/instance/instance_controller.go @@ -17,6 +17,7 @@ package instance import ( "context" + "errors" "fmt" "log" "strings" @@ -65,7 +66,7 @@ func (r *Reconciler) SetupWithManager( return nil } for _, instance := range instances.Items { - // Sanity check - lets make sure that this instance references the operatorVersion + // we need to pick only those instances, that belong to the OperatorVersion we're reconciling if instance.Spec.OperatorVersion.Name == obj.Meta.GetName() && instance.OperatorVersionNamespace() == obj.Meta.GetNamespace() { requests = append(requests, reconcile.Request{ @@ -244,7 +245,8 @@ func (r *Reconciler) handleError(err error, instance *kudov1alpha1.Instance) err } // for code being processed on instance, we need to handle these errors as well - if iError, ok := err.(*kudov1alpha1.InstanceError); ok { + var iError *kudov1alpha1.InstanceError + if errors.As(err, &iError) { if iError.EventName != nil { r.Recorder.Event(instance, "Warning", kudo.StringValue(iError.EventName), err.Error()) } diff --git a/pkg/controller/instance/plan_execution_engine.go b/pkg/controller/instance/plan_execution_engine.go index a8db1a3cf..2aff26e93 100644 --- a/pkg/controller/instance/plan_execution_engine.go +++ b/pkg/controller/instance/plan_execution_engine.go @@ -270,7 +270,7 @@ func prepareKubeResources(plan *activePlan, meta *executionMetadata, renderer ku phaseState.Status = v1alpha1.ExecutionFatalError stepState.Status = v1alpha1.ExecutionFatalError - err := errwrap.Wrapf(err, "error expanding template") + err := errwrap.Wrap(err, "error expanding template") log.Print(err) return nil, &executionError{err, true, nil} } From f24a5cc70dc638dfa8cabf40f31ef5c9c1b1fcb9 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Fri, 27 Sep 2019 16:42:05 +0200 Subject: [PATCH 24/29] Fix plan history CLI commands --- pkg/apis/kudo/v1alpha1/instance_types.go | 1 + pkg/kudoctl/cmd/init/crds.go | 92 +----------------------- pkg/kudoctl/cmd/plan.go | 4 +- pkg/kudoctl/cmd/plan/plan_history.go | 59 +++------------ 4 files changed, 17 insertions(+), 139 deletions(-) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index 3e16b82d8..c64690776 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -418,6 +418,7 @@ func init() { } // InstanceError indicates error on that can also emit a kubernetes warn event +// +k8s:deepcopy-gen=false type InstanceError struct { err error EventName *string // nil if no warn event should be created diff --git a/pkg/kudoctl/cmd/init/crds.go b/pkg/kudoctl/cmd/init/crds.go index 29e605c35..db223f299 100644 --- a/pkg/kudoctl/cmd/init/crds.go +++ b/pkg/kudoctl/cmd/init/crds.go @@ -26,9 +26,6 @@ func installCrds(client *apiextensionsclient.Clientset) error { if err := installInstance(client.ApiextensionsV1beta1()); err != nil { return err } - if err := installPlanExecution(client.ApiextensionsV1beta1()); err != nil { - return err - } return nil } @@ -50,12 +47,6 @@ func installInstance(client v1beta1.CustomResourceDefinitionsGetter) error { return err } -func installPlanExecution(client v1beta1.CustomResourceDefinitionsGetter) error { - pe := generatePlanExecution() - _, err := client.CustomResourceDefinitions().Create(pe) - return err -} - // operatorCrd provides the Operator CRD manifest for printing func operatorCrd() *apiextv1beta1.CustomResourceDefinition { crd := generateOperator() @@ -202,8 +193,8 @@ func generateInstance() *apiextv1beta1.CustomResourceDefinition { "parameters": apiextv1beta1.JSONSchemaProps{Type: "object"}, } statusProps := map[string]apiextv1beta1.JSONSchemaProps{ - "activePlan": apiextv1beta1.JSONSchemaProps{Type: "object"}, - "status": apiextv1beta1.JSONSchemaProps{Type: "string"}, + "planStatus": apiextv1beta1.JSONSchemaProps{Type: "object"}, + "aggregatedStatus": apiextv1beta1.JSONSchemaProps{Type: "object"}, } validationProps := map[string]apiextv1beta1.JSONSchemaProps{ @@ -225,82 +216,6 @@ func generateInstance() *apiextv1beta1.CustomResourceDefinition { return crd } -// planExecutionCrd provides the PlanExecution CRD manifest for printing -func planExecutionCrd() *apiextv1beta1.CustomResourceDefinition { - crd := generatePlanExecution() - crd.TypeMeta = metav1.TypeMeta{ - Kind: "CustomResourceDefinition", - APIVersion: "apiextensions.k8s.io/v1beta1", - } - return crd -} - -func generatePlanExecution() *apiextv1beta1.CustomResourceDefinition { - crd := generateCrd("PlanExecution", "planexecutions") - specProps := map[string]apiextv1beta1.JSONSchemaProps{ - "instance": apiextv1beta1.JSONSchemaProps{Type: "object"}, - "planName": apiextv1beta1.JSONSchemaProps{Type: "string"}, - "suspend": apiextv1beta1.JSONSchemaProps{Type: "boolean", Description: "This is copied from the CronJob Spec This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false."}, - } - - stepProps := map[string]apiextv1beta1.JSONSchemaProps{ - "delete": apiextv1beta1.JSONSchemaProps{Type: "boolean"}, - "name": apiextv1beta1.JSONSchemaProps{Type: "string"}, - "state": apiextv1beta1.JSONSchemaProps{Type: "string"}, - } - - phaseProps := map[string]apiextv1beta1.JSONSchemaProps{ - "name": apiextv1beta1.JSONSchemaProps{Type: "string"}, - "state": apiextv1beta1.JSONSchemaProps{Type: "string"}, - "steps": apiextv1beta1.JSONSchemaProps{ - Type: "array", - Description: "Steps maps a step name to a list of templates objects stored as a string", - Items: &apiextv1beta1.JSONSchemaPropsOrArray{Schema: &apiextv1beta1.JSONSchemaProps{ - Type: "object", - Properties: stepProps, - }, JSONSchemas: []apiextv1beta1.JSONSchemaProps{}}, - }, - "strategy": apiextv1beta1.JSONSchemaProps{Type: "string"}, - } - - statusProps := map[string]apiextv1beta1.JSONSchemaProps{ - "name": apiextv1beta1.JSONSchemaProps{Type: "string", Description: "INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run 'make' to regenerate code after modifying this file"}, - "phases": apiextv1beta1.JSONSchemaProps{ - Type: "array", - Description: "Phases maps a phase name to a Phase object", - Items: &apiextv1beta1.JSONSchemaPropsOrArray{Schema: &apiextv1beta1.JSONSchemaProps{ - Type: "object", - Required: []string{"steps"}, - Properties: phaseProps, - }, JSONSchemas: []apiextv1beta1.JSONSchemaProps{}}, - }, - "state": apiextv1beta1.JSONSchemaProps{Type: "string"}, - "strategy": apiextv1beta1.JSONSchemaProps{Type: "string"}, - } - - validationProps := map[string]apiextv1beta1.JSONSchemaProps{ - "apiVersion": apiextv1beta1.JSONSchemaProps{Type: "string"}, - "kind": apiextv1beta1.JSONSchemaProps{Type: "string"}, - "meta": apiextv1beta1.JSONSchemaProps{Type: "object"}, - "spec": apiextv1beta1.JSONSchemaProps{ - Properties: specProps, - Type: "object", - Required: []string{"planName", "instance"}, - }, - "status": apiextv1beta1.JSONSchemaProps{ - Type: "object", - Properties: statusProps, - }, - } - - crd.Spec.Validation = &apiextv1beta1.CustomResourceValidation{ - OpenAPIV3Schema: &apiextv1beta1.JSONSchemaProps{ - Properties: validationProps, - }, - } - return crd -} - // generateCrd provides a generic CRD object to be configured func generateCrd(kind string, plural string) *apiextv1beta1.CustomResourceDefinition { plural = strings.ToLower(plural) @@ -351,7 +266,6 @@ func CRDs() []runtime.Object { o := operatorCrd() ov := operatorVersionCrd() i := InstanceCrd() - pe := planExecutionCrd() - return []runtime.Object{o, ov, i, pe} + return []runtime.Object{o, ov, i} } diff --git a/pkg/kudoctl/cmd/plan.go b/pkg/kudoctl/cmd/plan.go index 5ca07f4eb..6f047fbd6 100644 --- a/pkg/kudoctl/cmd/plan.go +++ b/pkg/kudoctl/cmd/plan.go @@ -36,12 +36,12 @@ func NewPlanHistoryCmd() *cobra.Command { Short: "Lists history to a specific operator-version of an instance.", Example: planHistExample, RunE: func(cmd *cobra.Command, args []string) error { - return plan.RunHistory(cmd, args, options, &Settings) + return plan.RunHistory(cmd, options, &Settings) }, } listCmd.Flags().StringVar(&options.Instance, "instance", "", "The instance name.") - listCmd.Flags().StringVar(&options.Namespace, "namespace", "default", "The namespace where the operator watches for changes.") + listCmd.Flags().StringVar(&options.Namespace, "namespace", "default", "The namespace where the instance is running.") return listCmd } diff --git a/pkg/kudoctl/cmd/plan/plan_history.go b/pkg/kudoctl/cmd/plan/plan_history.go index 32ef23f50..abd23f186 100644 --- a/pkg/kudoctl/cmd/plan/plan_history.go +++ b/pkg/kudoctl/cmd/plan/plan_history.go @@ -1,17 +1,14 @@ package plan import ( - "encoding/json" "fmt" - kudov1alpha1 "github.com/kudobuilder/kudo/pkg/apis/kudo/v1alpha1" + "github.com/kudobuilder/kudo/pkg/kudoctl/util/kudo" + "k8s.io/apimachinery/pkg/api/errors" + "github.com/kudobuilder/kudo/pkg/kudoctl/env" "github.com/spf13/cobra" "github.com/xlab/treeprint" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/tools/clientcmd" ) // Options are the configurable options for plans @@ -26,63 +23,29 @@ var ( ) // RunHistory runs the plan history command -func RunHistory(cmd *cobra.Command, args []string, options *Options, settings *env.Settings) error { - +func RunHistory(cmd *cobra.Command, options *Options, settings *env.Settings) error { instanceFlag, err := cmd.Flags().GetString("instance") if err != nil || instanceFlag == "" { return fmt.Errorf("flag Error: Please set instance flag, e.g. \"--instance=\"") } - err = planHistory(args, options, settings) + err = planHistory(options, settings) if err != nil { return fmt.Errorf("client Error: %v", err) } return nil } -func planHistory(args []string, options *Options, settings *env.Settings) error { - - config, err := clientcmd.BuildConfigFromFlags("", settings.KubeConfig) - if err != nil { - return err - } - - // Create a Dynamic Client to interface with CRDs. - dynamicClient, err := dynamic.NewForConfig(config) - if err != nil { - return err - } - - planExecutionsGVR := schema.GroupVersionResource{ - Group: "kudo.dev", - Version: "v1alpha1", - Resource: "planexecutions", - } - - var labelSelector string - if len(args) == 0 { - fmt.Printf("History of all plan executions for instance \"%s\" in namespace \"%s\":\n", options.Instance, options.Namespace) - labelSelector = fmt.Sprintf("instance=%s", options.Instance) - } else { - fmt.Printf("History of plan-executions for instance \"%s\" in namespace \"%s\" for operator-version \"%s\":\n", options.Instance, options.Namespace, args[0]) - labelSelector = fmt.Sprintf("operator-version=%s, instance=%s", args[0], options.Instance) - } - - instObj, err := dynamicClient.Resource(planExecutionsGVR).Namespace(options.Namespace).List(metav1.ListOptions{ - LabelSelector: labelSelector, - }) +func planHistory(options *Options, settings *env.Settings) error { + kc, err := kudo.NewClient(settings.Namespace, settings.KubeConfig) if err != nil { + fmt.Printf("Unable to create kudo client to talk to kubernetes API server %w", err) return err } - - mInstObj, err := instObj.MarshalJSON() - if err != nil { - return err + instance, err := kc.GetInstance(options.Instance, options.Namespace) + if errors.IsNotFound(err) { + fmt.Printf("Instance %s/%s does not exist", instance.Namespace, instance.Name) } - - instance := kudov1alpha1.Instance{} - - err = json.Unmarshal(mInstObj, &instance) if err != nil { return err } From b38a24879b876848997f97f2f281b69c9186db29 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Fri, 27 Sep 2019 16:58:18 +0200 Subject: [PATCH 25/29] Update pkg/apis/kudo/v1alpha1/instance_types.go Co-Authored-By: Aleksey Dukhovniy --- pkg/apis/kudo/v1alpha1/instance_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index 7aab80522..de98dcb86 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -351,7 +351,7 @@ func planNameFromParameters(params []Parameter, ov *OperatorVersion) *string { // getParamDefinitions retrieves parameter metadata from OperatorVersion CRD func getParamDefinitions(params map[string]string, ov *OperatorVersion) []Parameter { - defs := make([]Parameter, 0) + defs := []Parameter{} for p1 := range params { for _, p2 := range ov.Spec.Parameters { if p2.Name == p1 { From d02a3f6a6b92bcefb3c2afa95d22486cab83baaf Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Fri, 27 Sep 2019 17:31:29 +0200 Subject: [PATCH 26/29] Update pkg/apis/kudo/v1alpha1/instance_types.go Co-Authored-By: Aleksey Dukhovniy --- pkg/apis/kudo/v1alpha1/instance_types.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index de98dcb86..5ae96f699 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -324,6 +324,7 @@ func (i *Instance) GetPlanToBeExecuted(ov *OperatorVersion) (*string, error) { } return plan, nil } + // did instance parameters change, so that the corresponding plan has to be triggered? if !reflect.DeepEqual(instanceSnapshot.Parameters, i.Spec.Parameters) { // instance updated log.Printf("Instance: instance %s/%s has updated parameters from %v to %v", i.Namespace, i.Name, instanceSnapshot.Parameters, i.Spec.Parameters) From 2eee2ee6a3c6d7a3d14495d5f2803db665d019ef Mon Sep 17 00:00:00 2001 From: Ken Sipe Date: Fri, 27 Sep 2019 17:30:52 -0500 Subject: [PATCH 27/29] updating golden file and fixing segfault in instance controller --- .../instance/instance_controller.go | 2 +- .../cmd/testdata/deploy-kudo.yaml.golden | 91 +------------------ 2 files changed, 3 insertions(+), 90 deletions(-) diff --git a/pkg/controller/instance/instance_controller.go b/pkg/controller/instance/instance_controller.go index f12ae1e1e..7c54536da 100644 --- a/pkg/controller/instance/instance_controller.go +++ b/pkg/controller/instance/instance_controller.go @@ -126,7 +126,7 @@ func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { instance, err := r.getInstance(request) if err != nil { if apierrors.IsNotFound(err) { // not retrying if instance not found, probably someone manually removed it? - log.Printf("Instance %s/%s not found, not retrying reconcile since this error is usually not recoverable (without manual intervention).", instance.Namespace, instance.Name) + log.Printf("Instances in namespace %s not found, not retrying reconcile since this error is usually not recoverable (without manual intervention).", request.NamespacedName) return reconcile.Result{}, nil } return reconcile.Result{}, err diff --git a/pkg/kudoctl/cmd/testdata/deploy-kudo.yaml.golden b/pkg/kudoctl/cmd/testdata/deploy-kudo.yaml.golden index 12d3b65c2..6e6ad58eb 100644 --- a/pkg/kudoctl/cmd/testdata/deploy-kudo.yaml.golden +++ b/pkg/kudoctl/cmd/testdata/deploy-kudo.yaml.golden @@ -255,97 +255,10 @@ spec: type: object status: properties: - activePlan: + aggregatedStatus: type: object - status: - type: string - type: object - version: v1alpha1 -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - creationTimestamp: null - labels: - app: kudo-manager - controller-tools.k8s.io: "1.0" - name: planexecutions.kudo.dev -spec: - group: kudo.dev - names: - kind: PlanExecution - plural: planexecutions - singular: planexecution - scope: Namespaced - validation: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - meta: - type: object - spec: - properties: - instance: + planStatus: type: object - planName: - type: string - suspend: - description: This is copied from the CronJob Spec This flag tells the - controller to suspend subsequent executions, it does not apply to - already started executions. Defaults to false. - type: boolean - required: - - planName - - instance - type: object - status: - properties: - name: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run ''make'' to regenerate code after modifying - this file' - type: string - phases: - description: Phases maps a phase name to a Phase object - items: - properties: - name: - type: string - state: - type: string - steps: - description: Steps maps a step name to a list of templates objects - stored as a string - items: - properties: - delete: - type: boolean - name: - type: string - state: - type: string - type: object - type: array - strategy: - type: string - required: - - steps - type: object - type: array - state: - type: string - strategy: - type: string type: object version: v1alpha1 status: From 33c5666e469ebefdc311fcb389c5fee9fdc5a1d2 Mon Sep 17 00:00:00 2001 From: Alena Varkockova Date: Mon, 30 Sep 2019 09:18:48 +0200 Subject: [PATCH 28/29] PR review comments --- pkg/apis/kudo/v1alpha1/instance_types.go | 48 ++++++++++++------- .../instance/instance_controller.go | 1 - pkg/util/health/ready.go | 2 +- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index 6e5b869c8..e7a16df4d 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -94,29 +94,40 @@ type StepStatus struct { // ExecutionStatus captures the state of the rollout. type ExecutionStatus string -// ExecutionInProgress actively deploying, but not yet healthy. -const ExecutionInProgress ExecutionStatus = "IN_PROGRESS" +const ( + // ExecutionInProgress actively deploying, but not yet healthy. + ExecutionInProgress ExecutionStatus = "IN_PROGRESS" -// ExecutionPending Not ready to deploy because dependent phases/steps not healthy. -const ExecutionPending ExecutionStatus = "PENDING" + // ExecutionPending Not ready to deploy because dependent phases/steps not healthy. + ExecutionPending ExecutionStatus = "PENDING" -// ExecutionComplete deployed and healthy. -const ExecutionComplete ExecutionStatus = "COMPLETE" + // ExecutionComplete deployed and healthy. + ExecutionComplete ExecutionStatus = "COMPLETE" -// ErrorStatus there was an error deploying the application. -const ErrorStatus ExecutionStatus = "ERROR" + // ErrorStatus there was an error deploying the application. + ErrorStatus ExecutionStatus = "ERROR" -// ExecutionFatalError there was an error deploying the application. -const ExecutionFatalError ExecutionStatus = "FATAL_ERROR" + // ExecutionFatalError there was an error deploying the application. + ExecutionFatalError ExecutionStatus = "FATAL_ERROR" -// ExecutionNeverRun is used when this plan/phase/step was never run so far -const ExecutionNeverRun ExecutionStatus = "NEVER_RUN" + // ExecutionNeverRun is used when this plan/phase/step was never run so far + ExecutionNeverRun ExecutionStatus = "NEVER_RUN" + + DeployPlanName = "deploy" + UpgradePlanName = "upgrade" + UpdatePlanName = "update" +) // IsTerminal returns true if the status is terminal (either complete, or in a nonrecoverable error) func (s ExecutionStatus) IsTerminal() bool { return s == ExecutionComplete || s == ExecutionFatalError } +// IsTerminal returns true if the status is terminal (either complete, or in a nonrecoverable error) +func (s ExecutionStatus) IsFinished() bool { + return s == ExecutionComplete +} + // IsRunning returns true if the plan is currently being executed func (s ExecutionStatus) IsRunning() bool { return s == ExecutionInProgress || s == ExecutionPending || s == ErrorStatus @@ -231,7 +242,6 @@ func (i *Instance) StartPlanExecution(planName string, ov *OperatorVersion) erro return &InstanceError{fmt.Errorf("asked to execute a plan %s but no such plan found in instance %s/%s", planName, i.Namespace, i.Name), kudo.String("PlanNotFound")} } - // TODO in the future when we again support manual plan execution, snapshot should be saved only manually executed plans err := i.SaveSnapshot() if err != nil { return err @@ -242,7 +252,7 @@ func (i *Instance) StartPlanExecution(planName string, ov *OperatorVersion) erro // isUpgradePlan returns true if this could be an upgrade plan - this is just an approximation because deploy plan can be used for both func isUpgradePlan(planName string) bool { - return planName == "deploy" || planName == "upgrade" + return planName == DeployPlanName || planName == UpgradePlanName } // UpdateInstanceStatus updates `Status.PlanStatus` and `Status.AggregatedStatus` property based on the given plan @@ -307,7 +317,7 @@ func (i *Instance) GetPlanToBeExecuted(ov *OperatorVersion) (*string, error) { // new instance, need to run deploy plan if i.NoPlanEverExecuted() { - return kudo.String("deploy"), nil + return kudo.String(DeployPlanName), nil } // did the instance change so that we need to run deploy/upgrade/update plan? @@ -315,10 +325,14 @@ func (i *Instance) GetPlanToBeExecuted(ov *OperatorVersion) (*string, error) { if err != nil { return nil, err } + if instanceSnapshot == nil { + // we don't have snapshot -> we never run deploy, also we cannot run update/upgrade. This should never happen + return nil, &InstanceError{fmt.Errorf("unexpected state: no plan is running, no snapshot present - this should never happen :) for instance %s/%s", i.Namespace, i.Name), kudo.String("UnexpectedState")} + } if instanceSnapshot.OperatorVersion.Name != i.Spec.OperatorVersion.Name { // this instance was upgraded to newer version log.Printf("Instance: instance %s/%s was upgraded from %s to %s operatorversion", i.Namespace, i.Name, instanceSnapshot.OperatorVersion.Name, i.Spec.OperatorVersion.Name) - plan := selectPlan([]string{"upgrade", "update", "deploy"}, ov) + plan := selectPlan([]string{UpgradePlanName, UpdatePlanName, DeployPlanName}, ov) if plan == nil { return nil, &InstanceError{fmt.Errorf("supposed to execute plan because instance %s/%s was upgraded but none of the deploy, upgrade, update plans found in linked operatorVersion", i.Namespace, i.Name), kudo.String("PlanNotFound")} } @@ -347,7 +361,7 @@ func planNameFromParameters(params []Parameter, ov *OperatorVersion) *string { return kudo.String(p.Trigger) } } - return selectPlan([]string{"update", "deploy"}, ov) + return selectPlan([]string{UpdatePlanName, DeployPlanName}, ov) } // getParamDefinitions retrieves parameter metadata from OperatorVersion CRD diff --git a/pkg/controller/instance/instance_controller.go b/pkg/controller/instance/instance_controller.go index 7c54536da..bf10a4180 100644 --- a/pkg/controller/instance/instance_controller.go +++ b/pkg/controller/instance/instance_controller.go @@ -162,7 +162,6 @@ func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) { activePlan, metadata, err := preparePlanExecution(instance, ov, activePlanStatus) if err != nil { - fmt.Printf("Pre-error instance %v", instance) err = r.handleError(err, instance) return reconcile.Result{}, err } diff --git a/pkg/util/health/ready.go b/pkg/util/health/ready.go index 78d73afcd..4f3a91c77 100644 --- a/pkg/util/health/ready.go +++ b/pkg/util/health/ready.go @@ -43,7 +43,7 @@ func IsHealthy(c client.Client, obj runtime.Object) error { case *kudov1alpha1.Instance: log.Printf("HealthUtil: Instance %v is in state %v", obj.Name, obj.Status.AggregatedStatus.Status) - if obj.Status.AggregatedStatus.Status == kudov1alpha1.ExecutionComplete { + if obj.Status.AggregatedStatus.Status.IsFinished() { return nil } return fmt.Errorf("instance's active plan is in state %v", obj.Status.AggregatedStatus.Status) From b2f841f0c1e6d5c140573d538d7b8c115b5e82be Mon Sep 17 00:00:00 2001 From: Ken Sipe Date: Mon, 30 Sep 2019 09:31:12 -0500 Subject: [PATCH 29/29] adding required godocs --- pkg/apis/kudo/v1alpha1/instance_types.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/apis/kudo/v1alpha1/instance_types.go b/pkg/apis/kudo/v1alpha1/instance_types.go index e7a16df4d..f5d34f1d1 100644 --- a/pkg/apis/kudo/v1alpha1/instance_types.go +++ b/pkg/apis/kudo/v1alpha1/instance_types.go @@ -113,8 +113,13 @@ const ( // ExecutionNeverRun is used when this plan/phase/step was never run so far ExecutionNeverRun ExecutionStatus = "NEVER_RUN" + // DeployPlanName is the name of the deployment plan DeployPlanName = "deploy" + + // UpgradePlanName is the name of the upgrade plan UpgradePlanName = "upgrade" + + // UpdatePlanName is the name of the update plan UpdatePlanName = "update" ) @@ -123,7 +128,7 @@ func (s ExecutionStatus) IsTerminal() bool { return s == ExecutionComplete || s == ExecutionFatalError } -// IsTerminal returns true if the status is terminal (either complete, or in a nonrecoverable error) +// IsFinished returns true if the status is complete regardless of errors func (s ExecutionStatus) IsFinished() bool { return s == ExecutionComplete }