Hello community, here is the log from the commit of package kured for openSUSE:Leap:15.2 checked in at 2020-03-09 18:12:47 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Leap:15.2/kured (Old) and /work/SRC/openSUSE:Leap:15.2/.kured.new.26092 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kured" Mon Mar 9 18:12:47 2020 rev:2 rq:781956 version:1.3.0 Changes: -------- --- /work/SRC/openSUSE:Leap:15.2/kured/kured.changes 2020-02-19 18:36:22.181686758 +0100 +++ /work/SRC/openSUSE:Leap:15.2/.kured.new.26092/kured.changes 2020-03-09 18:13:37.957389990 +0100 @@ -1,0 +2,12 @@ +Mon Mar 2 14:26:38 UTC 2020 - Thorsten Kukuk <[email protected]> + +- Update to version 1.3.0 + - Update k8s client tools to 1.15.x + - Ad Slack channel name configuration + - Add reboot window +- Obsoletes k8s-1.14.diff +- Remove kured-telemetrics.patch, chances that upstream accepts + any third party code are nearly zero. +- Update vendor.tar.xz + +------------------------------------------------------------------- Old: ---- k8s-1.14.diff kured-1.2.0.tar.gz kured-telemetrics.patch vendor.tar.gz New: ---- kured-1.3.0.tar.gz vendor.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kured.spec ++++++ --- /var/tmp/diff_new_pack.BL7ANT/_old 2020-03-09 18:13:39.553390764 +0100 +++ /var/tmp/diff_new_pack.BL7ANT/_new 2020-03-09 18:13:39.593390784 +0100 @@ -1,7 +1,7 @@ # # spec file for package kured # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -22,20 +22,18 @@ # Project name when using go tooling. %define project github.com/weaveworks/kured # Project upstream commit. -%define commit dcddbff +%define commit 2a0ad53 Name: kured -Version: 1.2.0 +Version: 1.3.0 Release: 0 Summary: Kubernetes daemonset to perform safe automatic node reboots License: Apache-2.0 Group: System/Management URL: https://github.com/weaveworks/kured Source: %{name}-%{version}.tar.gz -Source1: vendor.tar.gz -Patch: kured-telemetrics.patch -Patch1: k8s-1.14.diff +Source1: vendor.tar.xz BuildRequires: fdupes -BuildRequires: go >= 1.10 +BuildRequires: go = 1.12 BuildRequires: go-go-md2man ExcludeArch: s390 @@ -63,27 +61,15 @@ %prep %setup -qa1 -%patch -p1 -%patch1 -p1 %build - -# We can't use symlinks here because go-list gets confused by symlinks, so we -# have to copy the source to $HOME/go and then use that as the GOPATH. -export GOPATH=$HOME/go -mkdir -pv $HOME/go/src/%{project} -rm -rf $HOME/go/src/%{project}/* -cp -avr * $HOME/go/src/%{project} - # Build the binary. export VERSION=%{version} export COMMIT=%{commit} -export CGO_ENABLED=0 go build \ - -buildmode=pie \ + -mod vendor -buildmode=pie \ -ldflags "-s -w -X main.gitCommit=$COMMIT -X main.version=$VERSION" \ - -o %{name} \ - $HOME/go/src/%{project}/cmd/kured/*go + -o %{name} cmd/kured/*go %install # Install the binary. ++++++ kured-1.2.0.tar.gz -> kured-1.3.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/.circleci/config.yml new/kured-1.3.0/.circleci/config.yml --- old/kured-1.2.0/.circleci/config.yml 2019-05-16 12:29:26.000000000 +0200 +++ new/kured-1.3.0/.circleci/config.yml 2020-02-25 11:22:03.000000000 +0100 @@ -1,14 +1,11 @@ version: 2 jobs: build: - working_directory: /go/src/github.com/weaveworks/kured docker: - - image: circleci/golang:1.11 + - image: circleci/golang:1.12.5 steps: - checkout - setup_remote_docker - - run: go get github.com/golang/dep/cmd/dep - - run: dep ensure - deploy: name: Build and push image command: | diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/DEVELOPMENT.md new/kured-1.3.0/DEVELOPMENT.md --- old/kured-1.2.0/DEVELOPMENT.md 1970-01-01 01:00:00.000000000 +0100 +++ new/kured-1.3.0/DEVELOPMENT.md 2020-02-25 11:22:03.000000000 +0100 @@ -0,0 +1,106 @@ +# Developing `kured` + +We love contributions to `kured`, no matter if you are [helping out on +Slack][slack], reporting or triaging [issues][issues] or contributing code +to `kured`. + +In any case, it will make sense to familiarise yourself with the main +[README][readme] to understand the different features and options, which is +helpful for testing. The "building" section in particular makes sense if +you are planning to contribute code. + +[slack]: README.md#getting-help +[issues]: https://github.com/weaveworks/kured/issues +[readme]: README.md + +## Updating k8s support + +Whenever we want to update e.g. [`kubectl` in the +image](cmd/kured/Dockerfile), we need to consider if we update `client-go` +as well, some RBAC changes might be necessary too. + +This is what it took to support Kubernetes 1.14: +<https://github.com/weaveworks/kured/pull/75> + +That the process can be more involved that that can be seen in +<https://github.com/weaveworks/kured/commits/support-k8s-1.10> + +Once you updated everything, make sure you update the support matrix on +the main [README][readme] as well. + +## Release testing + +Before `kured` is released, we want to make sure it still works fine on the +previous, current and next minor version of Kubernetes (with respect to the +embedded `client-go` & `kubectl`). For local testing e.g. `minikube` can be +sufficient. + +Deploy kured in your test scenario, make sure you pass the right `image`, +update the e.g. `period` and `reboot-days` options, so you get immediate +results, if you login to a node and run: + +```console +sudo touch /var/run/reboot-required +``` + +### Testing with `minikube` + +A test-run with `minikube` could look like this: + +```console +minikube start --vm-driver kvm2 --kubernetes-version <k8s-release> + +# edit kured-ds.yaml to +# - point to new image +# - change e.g. period and reboot-days option for immediate results + +minikube kubectl -- apply -f kured-rbac.yaml +minikube kubectl -- apply -f kured-ds.yaml +minikube kubectl -- logs daemonset.apps/kured -n kube-system -f + +# In separate terminal +minikube ssh + sudo touch /var/run/reboot-required +minikube logs -f +``` + +Now check for the 'Commanding reboot' message and minikube going down. + +Unfortunately as of today, you are going to run into +<https://github.com/kubernetes/minikube/issues/2874>. This means that +minikube won't come back easily. You will need to start minikube again. +Then you can check for the lock release. + +If all the tests ran well, kured maintainers can reach out to the Weaveworks +team to get an upcoming `kured` release tested in the Dev environment for +real life testing. + +## Publishing a new kured release + +Check that `README.md` has an updated compatibility matrix and that the +url in the `kubectl` incantation (under "Installation") is updated to the +new version you want to release. + +Now create the `kured-<release>-dockerhub.yaml` for e.g. `1.3.0`: + +```sh +VERSION=1.3.0 +MANIFEST="kured-$VERSION-dockerhub.yaml" +cat kured-rbac.yaml > "$MANIFEST" +cat kured-ds.yaml >> "$MANIFEST" +sed -i "s#docker.io/weaveworks/kured#docker.io/weaveworks/kured:$VERSION#g" "$MANIFEST" +``` + +The last thing you need to do is update the `image:` to point to the release +tag, e.g. `docker.io/weaveworks/kured:1.3.0`. + +Now you can head to the Github UI, use the version number as tag and upload the +`kured-<release>-dockerhub.yaml` file. + +### Release notes + +Please describe what's new and noteworthy in the release notes, list the PRs +that landed and give a shout-out to everyone who contributed. + +Please also note down on which releases the upcoming `kured` release was +tested on. (Check old release notes if you're unsure.) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/Gopkg.lock new/kured-1.3.0/Gopkg.lock --- old/kured-1.2.0/Gopkg.lock 2019-05-16 12:29:26.000000000 +0200 +++ new/kured-1.3.0/Gopkg.lock 1970-01-01 01:00:00.000000000 +0100 @@ -1,412 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - name = "github.com/beorn7/perks" - packages = ["quantile"] - revision = "3a771d992973f24aa725d07868b467d1ddfceafb" - -[[projects]] - name = "github.com/gogo/protobuf" - packages = [ - "proto", - "sortkeys" - ] - revision = "4cbf7e384e768b4e01799441fdf2a706a5635ae7" - version = "v1.2.0" - -[[projects]] - name = "github.com/golang/protobuf" - packages = [ - "proto", - "ptypes", - "ptypes/any", - "ptypes/duration", - "ptypes/timestamp" - ] - revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" - version = "v1.2.0" - -[[projects]] - branch = "master" - name = "github.com/google/btree" - packages = ["."] - revision = "4030bb1f1f0c35b30ca7009e9ebd06849dd45306" - -[[projects]] - branch = "master" - name = "github.com/google/gofuzz" - packages = ["."] - revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1" - -[[projects]] - name = "github.com/googleapis/gnostic" - packages = [ - "OpenAPIv2", - "compiler", - "extensions" - ] - revision = "7c663266750e7d82587642f65e60bc4083f1f84e" - version = "v0.2.0" - -[[projects]] - branch = "master" - name = "github.com/gregjones/httpcache" - packages = [ - ".", - "diskcache" - ] - revision = "c63ab54fda8f77302f8d414e19933f2b6026a089" - -[[projects]] - name = "github.com/inconshreveable/mousetrap" - packages = ["."] - revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" - version = "v1.0" - -[[projects]] - name = "github.com/json-iterator/go" - packages = ["."] - revision = "1624edc4454b8682399def8740d46db5e4362ba4" - version = "v1.1.5" - -[[projects]] - name = "github.com/konsorten/go-windows-terminal-sequences" - packages = ["."] - revision = "5c8c8bd35d3832f5d134ae1e1e375b69a4d25242" - version = "v1.0.1" - -[[projects]] - name = "github.com/matttproud/golang_protobuf_extensions" - packages = ["pbutil"] - revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" - version = "v1.0.1" - -[[projects]] - name = "github.com/modern-go/concurrent" - packages = ["."] - revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" - version = "1.0.3" - -[[projects]] - name = "github.com/modern-go/reflect2" - packages = ["."] - revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" - version = "1.0.1" - -[[projects]] - branch = "master" - name = "github.com/petar/GoLLRB" - packages = ["llrb"] - revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4" - -[[projects]] - name = "github.com/peterbourgon/diskv" - packages = ["."] - revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" - version = "v2.0.1" - -[[projects]] - branch = "master" - name = "github.com/prometheus/client_golang" - packages = [ - "api", - "api/prometheus/v1", - "prometheus", - "prometheus/internal", - "prometheus/promhttp" - ] - revision = "fb3d5cb2ad5789367093b409855a3937d651b572" - -[[projects]] - branch = "master" - name = "github.com/prometheus/client_model" - packages = ["go"] - revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f" - -[[projects]] - branch = "master" - name = "github.com/prometheus/common" - packages = [ - "expfmt", - "internal/bitbucket.org/ww/goautoneg", - "model" - ] - revision = "67670fe90761d7ff18ec1d640135e53b9198328f" - -[[projects]] - branch = "master" - name = "github.com/prometheus/procfs" - packages = [ - ".", - "internal/util", - "nfs", - "xfs" - ] - revision = "14fa7590c24d4615893b68e22fce3b3489689f65" - -[[projects]] - name = "github.com/sirupsen/logrus" - packages = ["."] - revision = "bcd833dfe83d3cebad139e4a29ed79cb2318bf95" - version = "v1.2.0" - -[[projects]] - branch = "master" - name = "github.com/spf13/cobra" - packages = ["."] - revision = "d2d81d9a96e23f0255397222bb0b4e3165e492dc" - -[[projects]] - name = "github.com/spf13/pflag" - packages = ["."] - revision = "298182f68c66c05229eb03ac171abe6e309ee79a" - version = "v1.0.3" - -[[projects]] - branch = "master" - name = "golang.org/x/crypto" - packages = ["ssh/terminal"] - revision = "8d7daa0c54b357f3071e11eaef7efc4e19a417e2" - -[[projects]] - branch = "master" - name = "golang.org/x/net" - packages = [ - "context", - "context/ctxhttp", - "http/httpguts", - "http2", - "http2/hpack", - "idna" - ] - revision = "927f97764cc334a6575f4b7a1584a147864d5723" - -[[projects]] - branch = "master" - name = "golang.org/x/oauth2" - packages = [ - ".", - "internal" - ] - revision = "d668ce993890a79bda886613ee587a69dd5da7a6" - -[[projects]] - branch = "master" - name = "golang.org/x/sys" - packages = [ - "unix", - "windows" - ] - revision = "82a175fd1598e8a172e58ebdf5ed262bb29129e5" - -[[projects]] - name = "golang.org/x/text" - packages = [ - "collate", - "collate/build", - "internal/colltab", - "internal/gen", - "internal/tag", - "internal/triegen", - "internal/ucd", - "language", - "secure/bidirule", - "transform", - "unicode/bidi", - "unicode/cldr", - "unicode/norm", - "unicode/rangetable" - ] - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[[projects]] - branch = "master" - name = "golang.org/x/time" - packages = ["rate"] - revision = "85acf8d2951cb2a3bde7632f9ff273ef0379bcbd" - -[[projects]] - name = "google.golang.org/appengine" - packages = [ - "internal", - "internal/base", - "internal/datastore", - "internal/log", - "internal/remote_api", - "internal/urlfetch", - "urlfetch" - ] - revision = "e9657d882bb81064595ca3b56cbe2546bbabf7b1" - version = "v1.4.0" - -[[projects]] - name = "gopkg.in/inf.v0" - packages = ["."] - revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf" - version = "v0.9.1" - -[[projects]] - name = "gopkg.in/yaml.v2" - packages = ["."] - revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" - version = "v2.2.2" - -[[projects]] - branch = "master" - name = "k8s.io/api" - packages = [ - "admissionregistration/v1alpha1", - "admissionregistration/v1beta1", - "apps/v1", - "apps/v1beta1", - "apps/v1beta2", - "auditregistration/v1alpha1", - "authentication/v1", - "authentication/v1beta1", - "authorization/v1", - "authorization/v1beta1", - "autoscaling/v1", - "autoscaling/v2beta1", - "autoscaling/v2beta2", - "batch/v1", - "batch/v1beta1", - "batch/v2alpha1", - "certificates/v1beta1", - "coordination/v1beta1", - "core/v1", - "events/v1beta1", - "extensions/v1beta1", - "networking/v1", - "policy/v1beta1", - "rbac/v1", - "rbac/v1alpha1", - "rbac/v1beta1", - "scheduling/v1alpha1", - "scheduling/v1beta1", - "settings/v1alpha1", - "storage/v1", - "storage/v1alpha1", - "storage/v1beta1" - ] - revision = "173ce66c1e39d1d0f56e0b3347ff2988068aecd0" - -[[projects]] - branch = "release-1.13" - name = "k8s.io/apimachinery" - packages = [ - "pkg/api/errors", - "pkg/api/meta", - "pkg/api/resource", - "pkg/apis/meta/v1", - "pkg/apis/meta/v1/unstructured", - "pkg/apis/meta/v1beta1", - "pkg/conversion", - "pkg/conversion/queryparams", - "pkg/fields", - "pkg/labels", - "pkg/runtime", - "pkg/runtime/schema", - "pkg/runtime/serializer", - "pkg/runtime/serializer/json", - "pkg/runtime/serializer/protobuf", - "pkg/runtime/serializer/recognizer", - "pkg/runtime/serializer/streaming", - "pkg/runtime/serializer/versioning", - "pkg/selection", - "pkg/types", - "pkg/util/clock", - "pkg/util/errors", - "pkg/util/framer", - "pkg/util/intstr", - "pkg/util/json", - "pkg/util/naming", - "pkg/util/net", - "pkg/util/runtime", - "pkg/util/sets", - "pkg/util/validation", - "pkg/util/validation/field", - "pkg/util/yaml", - "pkg/version", - "pkg/watch", - "third_party/forked/golang/reflect" - ] - revision = "2b1284ed4c93a43499e781493253e2ac5959c4fd" - -[[projects]] - name = "k8s.io/client-go" - packages = [ - "discovery", - "kubernetes", - "kubernetes/scheme", - "kubernetes/typed/admissionregistration/v1alpha1", - "kubernetes/typed/admissionregistration/v1beta1", - "kubernetes/typed/apps/v1", - "kubernetes/typed/apps/v1beta1", - "kubernetes/typed/apps/v1beta2", - "kubernetes/typed/auditregistration/v1alpha1", - "kubernetes/typed/authentication/v1", - "kubernetes/typed/authentication/v1beta1", - "kubernetes/typed/authorization/v1", - "kubernetes/typed/authorization/v1beta1", - "kubernetes/typed/autoscaling/v1", - "kubernetes/typed/autoscaling/v2beta1", - "kubernetes/typed/autoscaling/v2beta2", - "kubernetes/typed/batch/v1", - "kubernetes/typed/batch/v1beta1", - "kubernetes/typed/batch/v2alpha1", - "kubernetes/typed/certificates/v1beta1", - "kubernetes/typed/coordination/v1beta1", - "kubernetes/typed/core/v1", - "kubernetes/typed/events/v1beta1", - "kubernetes/typed/extensions/v1beta1", - "kubernetes/typed/networking/v1", - "kubernetes/typed/policy/v1beta1", - "kubernetes/typed/rbac/v1", - "kubernetes/typed/rbac/v1alpha1", - "kubernetes/typed/rbac/v1beta1", - "kubernetes/typed/scheduling/v1alpha1", - "kubernetes/typed/scheduling/v1beta1", - "kubernetes/typed/settings/v1alpha1", - "kubernetes/typed/storage/v1", - "kubernetes/typed/storage/v1alpha1", - "kubernetes/typed/storage/v1beta1", - "pkg/apis/clientauthentication", - "pkg/apis/clientauthentication/v1alpha1", - "pkg/apis/clientauthentication/v1beta1", - "pkg/version", - "plugin/pkg/client/auth/exec", - "rest", - "rest/watch", - "tools/clientcmd/api", - "tools/metrics", - "tools/reference", - "transport", - "util/cert", - "util/connrotation", - "util/flowcontrol", - "util/integer" - ] - revision = "e64494209f554a6723674bd494d69445fb76a1d4" - version = "v10.0.0" - -[[projects]] - name = "k8s.io/klog" - packages = ["."] - revision = "a5bc97fbc634d635061f3146511332c7e313a55a" - version = "v0.1.0" - -[[projects]] - name = "sigs.k8s.io/yaml" - packages = ["."] - revision = "fd68e9863619f6ec2fdd8625fe1f02e7c877e480" - version = "v1.1.0" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - inputs-digest = "96704623ac96e94ce47b0820b4ff9e359b76c68a72eb83621a3de9d99d3d9d4f" - solver-name = "gps-cdcl" - solver-version = 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/Gopkg.toml new/kured-1.3.0/Gopkg.toml --- old/kured-1.2.0/Gopkg.toml 2019-05-16 12:29:26.000000000 +0200 +++ new/kured-1.3.0/Gopkg.toml 1970-01-01 01:00:00.000000000 +0100 @@ -1,27 +0,0 @@ -[[constraint]] - name = "github.com/sirupsen/logrus" - version = "v1.0.5" - -[[constraint]] - branch = "master" - name = "github.com/prometheus/client_golang" - -[[constraint]] - branch = "master" - name = "github.com/prometheus/common" - -[[constraint]] - branch = "master" - name = "github.com/spf13/cobra" - -[[constraint]] - name = "k8s.io/client-go" - version = "v10.0.0" - -[[constraint]] - name = "k8s.io/apimachinery" - branch = "release-1.13" - -[prune] - go-tests = true - unused-packages = true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/Makefile new/kured-1.3.0/Makefile --- old/kured-1.2.0/Makefile 2019-05-16 12:29:26.000000000 +0200 +++ new/kured-1.3.0/Makefile 2020-02-25 11:22:03.000000000 +0100 @@ -8,7 +8,6 @@ all: image clean: - go clean rm -f cmd/kured/kured rm -rf ./build diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/README.md new/kured-1.3.0/README.md --- old/kured-1.2.0/README.md 2019-05-16 12:29:26.000000000 +0200 +++ new/kured-1.3.0/README.md 2020-02-25 11:22:03.000000000 +0100 @@ -6,6 +6,7 @@ * [Installation](#installation) * [Configuration](#configuration) * [Reboot Sentinel File & Period](#reboot-sentinel-file-&-period) + * [Setting a schedule](#setting-a-schedule) * [Blocking Reboots via Alerts](#blocking-reboots-via-alerts) * [Blocking Reboots via Pods](#blocking-reboots-via-pods) * [Prometheus Metrics](#prometheus-metrics) @@ -40,7 +41,9 @@ | kured | kubectl | k8s.io/client-go | k8s.io/apimachinery | expected kubernetes compatibility | |--------|---------|------------------|---------------------|-----------------------------------| -| master | 1.13.6 | v10.0.0 | release-1.13 | 1.12.x, 1.13.x, 1.14.x | +| master | 1.15.10 | v12.0.0 | release-1.15 | 1.15.x, 1.16.x, 1.17.x | +| 1.3.0 | 1.15.10 | v12.0.0 | release-1.15 | 1.15.x, 1.16.x, 1.17.x | +| 1.2.0 | 1.13.6 | v10.0.0 | release-1.13 | 1.12.x, 1.13.x, 1.14.x | | 1.1.0 | 1.12.1 | v9.0.0 | release-1.12 | 1.11.x, 1.12.x, 1.13.x | | 1.0.0 | 1.7.6 | v4.0.0 | release-1.7 | 1.6.x, 1.7.x, 1.8.x | @@ -57,7 +60,7 @@ or Slack notifications: ``` -kubectl apply -f https://github.com/weaveworks/kured/releases/download/1.1.0/kured-1.1.0-dockerhub.yaml +kubectl apply -f https://github.com/weaveworks/kured/releases/download/1.3.0/kured-1.3.0-dockerhub.yaml ``` If you want to customise the installation, download the manifest and @@ -73,13 +76,18 @@ --blocking-pod-selector stringArray label selector identifying pods whose presence should prevent reboots --ds-name string name of daemonset on which to place lock (default "kured") --ds-namespace string namespace containing daemonset on which to place lock (default "kube-system") + --end-time string only reboot before this time of day (default "23:59") -h, --help help for kured --lock-annotation string annotation in which to record locking node (default "weave.works/kured-node-lock") --period duration reboot check period (default 1h0m0s) --prometheus-url string Prometheus instance to probe for active alerts + --reboot-days strings only reboot on these days (default [su,mo,tu,we,th,fr,sa]) --reboot-sentinel string path to file whose existence signals need to reboot (default "/var/run/reboot-required") + --slack-channel string slack channel for reboot notfications --slack-hook-url string slack hook URL for reboot notfications --slack-username string slack username for reboot notfications (default "kured") + --start-time string only reboot after this time of day (default "0:00") + --time-zone string use this timezone to calculate allowed reboot time (default "UTC") ``` ### Reboot Sentinel File & Period @@ -90,6 +98,29 @@ daemon uses a random offset derived from the period on startup so that nodes don't all contend for the lock simultaneously. +### Setting a schedule + +By default, kured will reboot any time it detects the sentinel, but this +may cause reboots during odd hours. While service disruption does not +normally occur, anything is possible and operators may want to restrict +reboots to predictable schedules. Use `--reboot-days`, `--start-time`, +`--end-time`, and `--time-zone` to set a schedule. For example, business +hours on the west coast USA can be specified with: + +``` + --reboot-days mon,tue,wed,thu,fri + --start-time 9am + --end-time 5pm + --time-zone America/Los_Angeles +``` + +Times can be formatted in numerous ways, including `5pm`, `5:00pm` `17:00`, +and `17`. `--time-zone` represents a Go `time.Location`, and can be `UTC`, +`Local`, or any entry in the standard Linux tz database. + +Note that when using smaller time windows, you should consider shortening +the sentinel check period (`--period`). + ### Blocking Reboots via Alerts You may find it desirable to block automatic node reboots when there @@ -229,9 +260,28 @@ ## Building +See the [CircleCI config](.circleci/config.yml) for the preferred +version of Golang. Kured now uses [Go +Modules](https://github.com/golang/go/wiki/Modules), so build +instructions vary depending on where you have checked out the +repository: + +**Building outside $GOPATH:** + +``` +make ``` -dep ensure && make + +**Building inside $GOPATH:** + ``` +GO111MODULE=on make +``` + +If you are interested in contributing code to kured, please take a look at +our [development][development] docs. + +[development]: DEVELOPMENT.md ## Frequently Asked/Anticipated Questions diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/cmd/kured/Dockerfile new/kured-1.3.0/cmd/kured/Dockerfile --- old/kured-1.2.0/cmd/kured/Dockerfile 2019-05-16 12:29:26.000000000 +0200 +++ new/kured-1.3.0/cmd/kured/Dockerfile 2020-02-25 11:22:03.000000000 +0100 @@ -1,7 +1,7 @@ -FROM alpine:3.8 -RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/* +FROM alpine:3.10.3 +RUN apk update && apk add ca-certificates tzdata && rm -rf /var/cache/apk/* # NB: you may need to update RBAC permissions when upgrading kubectl - see kured-rbac.yaml for details -ADD https://storage.googleapis.com/kubernetes-release/release/v1.13.6/bin/linux/amd64/kubectl /usr/bin/kubectl +ADD https://storage.googleapis.com/kubernetes-release/release/v1.15.10/bin/linux/amd64/kubectl /usr/bin/kubectl RUN chmod 0755 /usr/bin/kubectl COPY ./kured /usr/bin/kured ENTRYPOINT ["/usr/bin/kured"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/cmd/kured/main.go new/kured-1.3.0/cmd/kured/main.go --- old/kured-1.2.0/cmd/kured/main.go 2019-05-16 12:29:26.000000000 +0200 +++ new/kured-1.3.0/cmd/kured/main.go 2020-02-25 11:22:03.000000000 +0100 @@ -21,6 +21,7 @@ "github.com/weaveworks/kured/pkg/daemonsetlock" "github.com/weaveworks/kured/pkg/delaytick" "github.com/weaveworks/kured/pkg/notifications/slack" + "github.com/weaveworks/kured/pkg/timewindow" ) var ( @@ -36,8 +37,14 @@ rebootSentinel string slackHookURL string slackUsername string + slackChannel string podSelectors []string + rebootDays []string + rebootStart string + rebootEnd string + timezone string + // Metrics rebootRequiredGauge = prometheus.NewGaugeVec(prometheus.GaugeOpts{ Subsystem: "kured", @@ -75,10 +82,21 @@ "slack hook URL for reboot notfications") rootCmd.PersistentFlags().StringVar(&slackUsername, "slack-username", "kured", "slack username for reboot notfications") + rootCmd.PersistentFlags().StringVar(&slackChannel, "slack-channel", "", + "slack channel for reboot notfications") rootCmd.PersistentFlags().StringArrayVar(&podSelectors, "blocking-pod-selector", nil, "label selector identifying pods whose presence should prevent reboots") + rootCmd.PersistentFlags().StringSliceVar(&rebootDays, "reboot-days", timewindow.EveryDay, + "schedule reboot on these days") + rootCmd.PersistentFlags().StringVar(&rebootStart, "start-time", "0:00", + "schedule reboot only after this time of day") + rootCmd.PersistentFlags().StringVar(&rebootEnd, "end-time", "23:59:59", + "schedule reboot only before this time of day") + rootCmd.PersistentFlags().StringVar(&timezone, "time-zone", "UTC", + "use this timezone for schedule inputs") + if err := rootCmd.Execute(); err != nil { log.Fatal(err) } @@ -212,7 +230,7 @@ log.Infof("Draining node %s", nodeID) if slackHookURL != "" { - if err := slack.NotifyDrain(slackHookURL, slackUsername, nodeID); err != nil { + if err := slack.NotifyDrain(slackHookURL, slackUsername, slackChannel, nodeID); err != nil { log.Warnf("Error notifying slack: %v", err) } } @@ -237,7 +255,7 @@ log.Infof("Commanding reboot") if slackHookURL != "" { - if err := slack.NotifyReboot(slackHookURL, slackUsername, nodeID); err != nil { + if err := slack.NotifyReboot(slackHookURL, slackUsername, slackChannel, nodeID); err != nil { log.Warnf("Error notifying slack: %v", err) } } @@ -265,7 +283,7 @@ Unschedulable bool `json:"unschedulable"` } -func rebootAsRequired(nodeID string) { +func rebootAsRequired(nodeID string, window *timewindow.TimeWindow) { config, err := rest.InClusterConfig() if err != nil { log.Fatal(err) @@ -289,7 +307,7 @@ source := rand.NewSource(time.Now().UnixNano()) tick := delaytick.New(source, period) for _ = range tick { - if rebootRequired() && !rebootBlocked(client, nodeID) { + if window.Contains(time.Now()) && rebootRequired() && !rebootBlocked(client, nodeID) { node, err := client.CoreV1().Nodes().Get(nodeID, metav1.GetOptions{}) if err != nil { log.Fatal(err) @@ -318,12 +336,18 @@ log.Fatal("KURED_NODE_ID environment variable required") } + window, err := timewindow.New(rebootDays, rebootStart, rebootEnd, timezone) + if err != nil { + log.Fatalf("Failed to build time window: %v", err) + } + log.Infof("Node ID: %s", nodeID) log.Infof("Lock Annotation: %s/%s:%s", dsNamespace, dsName, lockAnnotation) log.Infof("Reboot Sentinel: %s every %v", rebootSentinel, period) log.Infof("Blocking Pod Selectors: %v", podSelectors) + log.Infof("Reboot on: %v", window) - go rebootAsRequired(nodeID) + go rebootAsRequired(nodeID, window) go maintainRebootRequiredMetric(nodeID) http.Handle("/metrics", promhttp.Handler()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/go.mod new/kured-1.3.0/go.mod --- old/kured-1.2.0/go.mod 1970-01-01 01:00:00.000000000 +0100 +++ new/kured-1.3.0/go.mod 2020-02-25 11:22:03.000000000 +0100 @@ -0,0 +1,23 @@ +module github.com/weaveworks/kured + +go 1.12 + +require ( + github.com/gogo/protobuf v1.2.0 // indirect + github.com/googleapis/gnostic v0.2.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/json-iterator/go v1.1.5 // indirect + github.com/prometheus/client_golang v0.0.0-20181230203121-fb3d5cb2ad57 + github.com/prometheus/common v0.0.0-20181218105931-67670fe90761 + github.com/prometheus/procfs v0.0.0-20190102135031-14fa7590c24d // indirect + github.com/sirupsen/logrus v1.2.0 + github.com/spf13/cobra v0.0.0-20181127133106-d2d81d9a96e2 + github.com/spf13/pflag v1.0.3 // indirect + golang.org/x/crypto v0.0.0-20190102171810-8d7daa0c54b3 // indirect + golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.2.2 // indirect + k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719 + k8s.io/client-go v12.0.0+incompatible + k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5 // indirect +) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/go.sum new/kured-1.3.0/go.sum --- old/kured-1.2.0/go.sum 1970-01-01 01:00:00.000000000 +0100 +++ new/kured-1.3.0/go.sum 2020-02-25 11:22:03.000000000 +0100 @@ -0,0 +1,132 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= +github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5/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/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/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= +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/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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.0.0-20181230203121-fb3d5cb2ad57 h1:+cstagqzfjiYrs1QrmypZRndHJafhB9W3ab/zPiwl1Q= +github.com/prometheus/client_golang v0.0.0-20181230203121-fb3d5cb2ad57/go.mod h1:PS8H/EtMiYNSR3o4jWYMuyp9FkDf2ptwxn1kTerl5EM= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20181218105931-67670fe90761 h1:z6tvbDJ5OLJ48FFmnksv04a78maSTRBUIhkdHYV5Y98= +github.com/prometheus/common v0.0.0-20181218105931-67670fe90761/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190102135031-14fa7590c24d h1:iSxvUGRUGQTUgEo5+8TqMVQoH5AdaphEIjh8AnM7TPo= +github.com/prometheus/procfs v0.0.0-20190102135031-14fa7590c24d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cobra v0.0.0-20181127133106-d2d81d9a96e2 h1:gPjqutnNhLnq5t4hT2UfL06h/mvp4je4OY7Dk71vhuk= +github.com/spf13/cobra v0.0.0-20181127133106-d2d81d9a96e2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.1/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/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/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= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190102171810-8d7daa0c54b3 h1:35ZwriXqdZtBGoFgUpW71Z7xz5o23fRpWHFAO2PlnIA= +golang.org/x/crypto v0.0.0-20190102171810-8d7daa0c54b3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/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-20181217023233-e147a9138326/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 h1:bfLnR+k0tq5Lqt6dflRLcZiz6UaXCMt3vhYJ1l4FQ80= +golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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= +golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +k8s.io/api v0.0.0-20190620084959-7cf5895f2711 h1:BblVYz/wE5WtBsD/Gvu54KyBUTJMflolzc5I2DTvh50= +k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A= +k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719 h1:uV4S5IB5g4Nvi+TBVNf3e9L4wrirlwYJ6w88jUQxTUw= +k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA= +k8s.io/client-go v12.0.0+incompatible h1:YlJxncpeVUC98/WMZKC3JZGk/OXQWCZjAB4Xr3B17RY= +k8s.io/client-go v12.0.0+incompatible/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k= +k8s.io/klog v0.3.0 h1:0VPpR+sizsiivjIfIAQH/rl8tan6jvWkS7lU+0di3lE= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68= +k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= +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= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/kured-ds.yaml new/kured-1.3.0/kured-ds.yaml --- old/kured-1.2.0/kured-ds.yaml 2019-05-16 12:29:26.000000000 +0200 +++ new/kured-1.3.0/kured-ds.yaml 2020-02-25 11:22:03.000000000 +0100 @@ -29,10 +29,9 @@ restartPolicy: Always containers: - name: kured - image: docker.io/weaveworks/kured # If you find yourself here - # wondering why there is no - # :latest tag on Docker Hub, - # see the FAQ in the README + image: docker.io/weaveworks/kured + # If you find yourself here wondering why there is no + # :latest tag on Docker Hub,see the FAQ in the README imagePullPolicy: IfNotPresent securityContext: privileged: true # Give permission to nsenter /proc/1/ns/mnt @@ -51,9 +50,14 @@ # - --blocking-pod-selector=... # - --ds-name=kured # - --ds-namespace=kube-system +# - --end-time=23:59:59 # - --lock-annotation=weave.works/kured-node-lock # - --period=1h # - --prometheus-url=http://prometheus.monitoring.svc.cluster.local +# - --reboot-days=sun,mon,tue,wed,thu,fri,sat # - --reboot-sentinel=/var/run/reboot-required # - --slack-hook-url=https://hooks.slack.com/... # - --slack-username=prod +# - --slack-channel=alerting +# - --start-time=0:00 +# - --time-zone=UTC diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/kured-rbac.yaml new/kured-1.3.0/kured-rbac.yaml --- old/kured-1.2.0/kured-rbac.yaml 2019-05-16 12:29:26.000000000 +0200 +++ new/kured-1.3.0/kured-rbac.yaml 2020-02-25 11:22:03.000000000 +0100 @@ -8,7 +8,7 @@ # Allow kubectl to drain/uncordon # # NB: These permissions are tightly coupled to the bundled version of kubectl; the ones below -# match https://github.com/kubernetes/kubernetes/blob/v1.12.1/pkg/kubectl/cmd/drain.go +# match https://github.com/kubernetes/kubernetes/blob/v1.14.1/pkg/kubectl/cmd/drain/drain.go # - apiGroups: [""] resources: ["nodes"] @@ -16,7 +16,7 @@ - apiGroups: [""] resources: ["pods"] verbs: ["list","delete","get"] -- apiGroups: ["extensions"] +- apiGroups: ["apps"] resources: ["daemonsets"] verbs: ["get"] - apiGroups: [""] @@ -43,7 +43,7 @@ name: kured rules: # Allow kured to lock/unlock itself -- apiGroups: ["extensions"] +- apiGroups: ["apps"] resources: ["daemonsets"] resourceNames: ["kured"] verbs: ["update"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/pkg/daemonsetlock/daemonsetlock.go new/kured-1.3.0/pkg/daemonsetlock/daemonsetlock.go --- old/kured-1.2.0/pkg/daemonsetlock/daemonsetlock.go 2019-05-16 12:29:26.000000000 +0200 +++ new/kured-1.3.0/pkg/daemonsetlock/daemonsetlock.go 2020-02-25 11:22:03.000000000 +0100 @@ -29,7 +29,7 @@ func (dsl *DaemonSetLock) Acquire(metadata interface{}) (acquired bool, owner string, err error) { for { - ds, err := dsl.client.ExtensionsV1beta1().DaemonSets(dsl.namespace).Get(dsl.name, metav1.GetOptions{}) + ds, err := dsl.client.AppsV1().DaemonSets(dsl.namespace).Get(dsl.name, metav1.GetOptions{}) if err != nil { return false, "", err } @@ -53,7 +53,7 @@ } ds.ObjectMeta.Annotations[dsl.annotation] = string(valueBytes) - _, err = dsl.client.ExtensionsV1beta1().DaemonSets(dsl.namespace).Update(ds) + _, err = dsl.client.AppsV1().DaemonSets(dsl.namespace).Update(ds) if err != nil { if se, ok := err.(*errors.StatusError); ok && se.ErrStatus.Reason == metav1.StatusReasonConflict { // Something else updated the resource between us reading and writing - try again soon @@ -68,7 +68,7 @@ } func (dsl *DaemonSetLock) Test(metadata interface{}) (holding bool, err error) { - ds, err := dsl.client.ExtensionsV1beta1().DaemonSets(dsl.namespace).Get(dsl.name, metav1.GetOptions{}) + ds, err := dsl.client.AppsV1().DaemonSets(dsl.namespace).Get(dsl.name, metav1.GetOptions{}) if err != nil { return false, err } @@ -87,7 +87,7 @@ func (dsl *DaemonSetLock) Release() error { for { - ds, err := dsl.client.ExtensionsV1beta1().DaemonSets(dsl.namespace).Get(dsl.name, metav1.GetOptions{}) + ds, err := dsl.client.AppsV1().DaemonSets(dsl.namespace).Get(dsl.name, metav1.GetOptions{}) if err != nil { return err } @@ -107,7 +107,7 @@ delete(ds.ObjectMeta.Annotations, dsl.annotation) - _, err = dsl.client.ExtensionsV1beta1().DaemonSets(dsl.namespace).Update(ds) + _, err = dsl.client.AppsV1().DaemonSets(dsl.namespace).Update(ds) if err != nil { if se, ok := err.(*errors.StatusError); ok && se.ErrStatus.Reason == metav1.StatusReasonConflict { // Something else updated the resource between us reading and writing - try again soon diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/pkg/notifications/slack/slack.go new/kured-1.3.0/pkg/notifications/slack/slack.go --- old/kured-1.2.0/pkg/notifications/slack/slack.go 2019-05-16 12:29:26.000000000 +0200 +++ new/kured-1.3.0/pkg/notifications/slack/slack.go 2020-02-25 11:22:03.000000000 +0100 @@ -15,12 +15,14 @@ type body struct { Text string `json:"text,omitempty"` Username string `json:"username,omitempty"` + Channel string `json:"channel,omitempty"` } -func notify(hookURL, username, message string) error { +func notify(hookURL, username, channel, message string) error { msg := body{ Text: message, Username: username, + Channel: channel, } var buf bytes.Buffer @@ -41,10 +43,10 @@ return nil } -func NotifyDrain(hookURL, username, nodeID string) error { - return notify(hookURL, username, fmt.Sprintf("Draining node %s", nodeID)) +func NotifyDrain(hookURL, username, channel, nodeID string) error { + return notify(hookURL, username, channel, fmt.Sprintf("Draining node %s", nodeID)) } -func NotifyReboot(hookURL, username, nodeID string) error { - return notify(hookURL, username, fmt.Sprintf("Rebooting node %s", nodeID)) +func NotifyReboot(hookURL, username, channel, nodeID string) error { + return notify(hookURL, username, channel, fmt.Sprintf("Rebooting node %s", nodeID)) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/pkg/timewindow/days.go new/kured-1.3.0/pkg/timewindow/days.go --- old/kured-1.2.0/pkg/timewindow/days.go 1970-01-01 01:00:00.000000000 +0100 +++ new/kured-1.3.0/pkg/timewindow/days.go 2020-02-25 11:22:03.000000000 +0100 @@ -0,0 +1,91 @@ +package timewindow + +import ( + "fmt" + "strconv" + "strings" + "time" +) + +var EveryDay = []string{"su", "mo", "tu", "we", "th", "fr", "sa"} + +// dayStrings maps day strings to time.Weekdays +var dayStrings = map[string]time.Weekday{ + "su": time.Sunday, + "sun": time.Sunday, + "sunday": time.Sunday, + "mo": time.Monday, + "mon": time.Monday, + "monday": time.Monday, + "tu": time.Tuesday, + "tue": time.Tuesday, + "tuesday": time.Tuesday, + "we": time.Wednesday, + "wed": time.Wednesday, + "wednesday": time.Wednesday, + "th": time.Thursday, + "thu": time.Thursday, + "thursday": time.Thursday, + "fr": time.Friday, + "fri": time.Friday, + "friday": time.Friday, + "sa": time.Saturday, + "sat": time.Saturday, + "saturday": time.Saturday, +} + +type weekdays uint32 + +// parseWeekdays creates a set of weekdays from a string slice +func parseWeekdays(days []string) (weekdays, error) { + var result uint32 + for _, day := range days { + if len(day) == 0 { + continue + } + + weekday, err := parseWeekday(day) + if err != nil { + return weekdays(0), err + } + + result |= 1 << uint32(weekday) + } + + return weekdays(result), nil +} + +// Contains returns true if the specified weekday is a member of this set. +func (w weekdays) Contains(day time.Weekday) bool { + return uint32(w)&(1<<uint32(day)) != 0 +} + +// String returns a string representation of the set of weekdays. +func (w weekdays) String() string { + var b strings.Builder + for i := uint32(0); i < 7; i++ { + if uint32(w)&(1<<i) != 0 { + b.WriteString(time.Weekday(i).String()[0:3]) + } else { + b.WriteString("---") + } + } + + return b.String() +} + +func parseWeekday(day string) (time.Weekday, error) { + if n, err := strconv.Atoi(day); err == nil { + if n >= 0 && n < 7 { + return time.Weekday(n), nil + } else { + return time.Sunday, fmt.Errorf("Invalid weekday, number out of range: %s", day) + } + } + + if weekday, ok := dayStrings[strings.ToLower(day)]; ok { + return weekday, nil + } else { + return time.Sunday, fmt.Errorf("Invalid weekday: %s", day) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/pkg/timewindow/days_test.go new/kured-1.3.0/pkg/timewindow/days_test.go --- old/kured-1.2.0/pkg/timewindow/days_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/kured-1.3.0/pkg/timewindow/days_test.go 2020-02-25 11:22:03.000000000 +0100 @@ -0,0 +1,46 @@ +package timewindow + +import ( + "strings" + "testing" +) + +func TestParseWeekdays(t *testing.T) { + tests := []struct { + input string + result string + }{ + {"0,4", "Sun---------Thu------"}, + {"su,mo,tu", "SunMonTue------------"}, + {"sunday,tu,thu", "Sun---Tue---Thu------"}, + {"THURSDAY", "------------Thu------"}, + {"we,WED,WeDnEsDaY", "---------Wed---------"}, + {"", "---------------------"}, + {",,,", "---------------------"}, + } + + for _, tst := range tests { + res, err := parseWeekdays(strings.Split(tst.input, ",")) + if err != nil { + t.Errorf("Received error for input %s: %v", tst.input, err) + } else if res.String() != tst.result { + t.Errorf("Test %s: Expected %s got %s", tst.input, tst.result, res.String()) + } + } +} + +func TestParseWeekdaysErrors(t *testing.T) { + tests := []string{ + "15", + "-8", + "8", + "mon,tue,wed,fridayyyy", + } + + for _, tst := range tests { + _, err := parseWeekdays(strings.Split(tst, ",")) + if err == nil { + t.Errorf("Expected to receive error for input %s", tst) + } + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/pkg/timewindow/timewindow.go new/kured-1.3.0/pkg/timewindow/timewindow.go --- old/kured-1.2.0/pkg/timewindow/timewindow.go 1970-01-01 01:00:00.000000000 +0100 +++ new/kured-1.3.0/pkg/timewindow/timewindow.go 2020-02-25 11:22:03.000000000 +0100 @@ -0,0 +1,68 @@ +package timewindow + +import ( + "fmt" + "time" +) + +// TimeWindow specifies a schedule of days and times. +type TimeWindow struct { + days weekdays + location *time.Location + startTime time.Time + endTime time.Time +} + +// New creates a TimeWindow instance based on string inputs specifying a schedule. +func New(days []string, startTime, endTime, location string) (*TimeWindow, error) { + tw := &TimeWindow{} + + var err error + if tw.days, err = parseWeekdays(days); err != nil { + return nil, err + } + + if tw.location, err = time.LoadLocation(location); err != nil { + return nil, err + } + + if tw.startTime, err = parseTime(startTime, tw.location); err != nil { + return nil, err + } + + if tw.endTime, err = parseTime(endTime, tw.location); err != nil { + return nil, err + } + + return tw, nil +} + +// Contains determines whether the specified time is within this time window. +func (tw *TimeWindow) Contains(t time.Time) bool { + loctime := t.In(tw.location) + if !tw.days.Contains(loctime.Weekday()) { + return false + } + + start := time.Date(loctime.Year(), loctime.Month(), loctime.Day(), tw.startTime.Hour(), tw.startTime.Minute(), tw.startTime.Second(), 0, tw.location) + end := time.Date(loctime.Year(), loctime.Month(), loctime.Day(), tw.endTime.Hour(), tw.endTime.Minute(), tw.endTime.Second(), 1e9-1, tw.location) + + return (loctime.After(start) || loctime.Equal(start)) && (loctime.Before(end) || loctime.Equal(end)) +} + +// String returns a string representation of this time window. +func (tw *TimeWindow) String() string { + return fmt.Sprintf("%s between %02d:%02d and %02d:%02d %s", tw.days.String(), tw.startTime.Hour(), tw.startTime.Minute(), tw.endTime.Hour(), tw.endTime.Minute(), tw.location.String()) +} + +// parseTime tries to parse a time with several formats. +func parseTime(s string, loc *time.Location) (time.Time, error) { + fmts := []string{"15:04", "15:04:05", "03:04pm", "15", "03pm", "3pm"} + for _, f := range fmts { + if t, err := time.ParseInLocation(f, s, loc); err == nil { + return t, nil + } + } + + return time.Now(), fmt.Errorf("Invalid time format: %s", s) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kured-1.2.0/pkg/timewindow/timewindow_test.go new/kured-1.3.0/pkg/timewindow/timewindow_test.go --- old/kured-1.2.0/pkg/timewindow/timewindow_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/kured-1.3.0/pkg/timewindow/timewindow_test.go 2020-02-25 11:22:03.000000000 +0100 @@ -0,0 +1,60 @@ +package timewindow + +import ( + "strings" + "testing" + "time" +) + +func TestTimeWindows(t *testing.T) { + type testcase struct { + time string + result bool + } + + tests := []struct { + days string + start string + end string + loc string + cases []testcase + }{ + {"mon,tue,wed,thu,fri", "9am", "5pm", "America/Los_Angeles", []testcase{ + {"2019/04/04 00:49 PDT", false}, + {"2019/04/05 08:59 PDT", false}, + {"2019/04/05 9:01 PDT", true}, + {"2019/03/31 10:00 PDT", false}, + {"2019/04/04 12:00 PDT", true}, + {"2019/04/04 11:59 UTC", false}, + }}, + {"mon,we,fri", "10:01", "11:30am", "America/Los_Angeles", []testcase{ + {"2019/04/05 10:30 PDT", true}, + {"2019/04/06 10:30 PDT", false}, + {"2019/04/07 10:30 PDT", false}, + {"2019/04/08 10:30 PDT", true}, + {"2019/04/09 10:30 PDT", false}, + {"2019/04/10 10:30 PDT", true}, + {"2019/04/11 10:30 PDT", false}, + }}, + {"mo,tu,we,th,fr", "00:00", "23:59:59", "UTC", []testcase{ + {"2019/04/18 00:00 UTC", true}, + {"2019/04/18 23:59 UTC", true}, + }}, + } + + for i, tst := range tests { + tw, err := New(strings.Split(tst.days, ","), tst.start, tst.end, tst.loc) + if err != nil { + t.Errorf("Test [%d] failed to create TimeWindow: %v", i, err) + } + + for _, cas := range tst.cases { + tm, err := time.ParseInLocation("2006/01/02 15:04 MST", cas.time, tw.location) + if err != nil { + t.Errorf("Failed to parse time \"%s\": %v", cas.time, err) + } else if cas.result != tw.Contains(tm) { + t.Errorf("(%s) contains (%s) didn't match expected result of %v", tw.String(), cas.time, cas.result) + } + } + } +}
