From 23233c4a0bb25232ca6301da1b3fb0b83ee2c513 Mon Sep 17 00:00:00 2001 From: Tonnis Wildeboer Date: Wed, 6 Dec 2017 20:33:37 -0800 Subject: [PATCH 1/4] Add support to configure times to suspend chaos. --- Dockerfile | 2 + README.md | 20 + examples/helm/chaoskube/Chart.yaml | 11 + examples/helm/chaoskube/README.md | 71 +++ examples/helm/chaoskube/templates/NOTES.txt | 9 + .../helm/chaoskube/templates/deployment.yaml | 46 ++ examples/helm/chaoskube/values.yaml | 50 ++ examples/{ => kubernetes}/chaoskube.yaml | 15 +- examples/{ => kubernetes}/rbac.yaml | 0 main.go | 274 +++++++++- main_test.go | 504 ++++++++++++++++++ 11 files changed, 989 insertions(+), 13 deletions(-) create mode 100755 examples/helm/chaoskube/Chart.yaml create mode 100755 examples/helm/chaoskube/README.md create mode 100755 examples/helm/chaoskube/templates/NOTES.txt create mode 100755 examples/helm/chaoskube/templates/deployment.yaml create mode 100755 examples/helm/chaoskube/values.yaml rename examples/{ => kubernetes}/chaoskube.yaml (51%) rename examples/{ => kubernetes}/rbac.yaml (100%) create mode 100644 main_test.go diff --git a/Dockerfile b/Dockerfile index 1dde631c..1ab6a840 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,8 @@ RUN go build -o /bin/chaoskube -v \ FROM alpine:3.6 MAINTAINER Linki +RUN apk --no-cache add tzdata + RUN addgroup -S chaoskube && adduser -S -g chaoskube chaoskube COPY --from=builder /bin/chaoskube /bin/chaoskube diff --git a/README.md b/README.md index 4828d1a1..4863248a 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,26 @@ spec: ... ``` +## Limiting the Chaos + +You can limit when chaos introduced. To turn on the feature, add the `--limit-chaos` option and the `--location` option, which requires a timezone name from the [(IANA) tz databse](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). Alternatively, you can use `UTC` or `Local` as the location. +By default, this will only allow chaos to be introduced between 9:30 and 14:30, and not on Saturday or Sunday. You can also explicitly add a list of `YYYY-MM-DD`-formatted dates as "holidays". See the options chart below for more details. + +## Options + +| Option | Description | Default | +|------------------|---------------------------------------------------------------------|------------------------| +| `--interval` | interval between pod terminations | 10m | +| `--labels` | label selector to filter pods by | (matches everything) | +| `--annotations` | annotation selector to filter pods by | (matches everything) | +| `--namespaces` | namespace selector to filter pods by | (all namespaces) | +| `--dry-run` | don't kill pods, only log what would have been done | true | +| `--limit-chaos` | limit chaos according to specified times/days | false | +| `--location` | timezone from tz database, e.g "America/New_York", "UTC" or "Local" | (none) | +| `--off-days` | days when chaos is to be suspended. (Or "none") | "Saturday,Sunday" | +| `--chaos-hrs` | start and end time for introducing chaos (24hr time) | "start:9:30,end:14:30" | +| `--holidays` | comma-separated, "YYYY-MM-DD" days to skip chaos | (empty list) | + ## Contributing Feel free to create issues or submit pull requests. diff --git a/examples/helm/chaoskube/Chart.yaml b/examples/helm/chaoskube/Chart.yaml new file mode 100755 index 00000000..0635ea43 --- /dev/null +++ b/examples/helm/chaoskube/Chart.yaml @@ -0,0 +1,11 @@ +appVersion: 0.6.2 +description: Chaoskube periodically kills random pods in your Kubernetes cluster. +engine: gotpl +home: https://github.com/linki/chaoskube +maintainers: +- email: linki+kubernetes.io@posteo.de + name: Martin Linkhorst +name: chaoskube +sources: +- https://github.com/linki/chaoskube +version: 0.6.2 diff --git a/examples/helm/chaoskube/README.md b/examples/helm/chaoskube/README.md new file mode 100755 index 00000000..669b897a --- /dev/null +++ b/examples/helm/chaoskube/README.md @@ -0,0 +1,71 @@ +# Chaoskube Helm Chart + +[chaoskube](https://github.com/linki/chaoskube) periodically kills random pods in your Kubernetes cluster. + +## TL;DR; + +```console +$ helm install stable/chaoskube +``` + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```console +$ helm install --name my-release stable/chaoskube +``` + +The command deploys chaoskube on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation. + +## Uninstalling the Chart + +To uninstall/delete the my-release deployment: + +```console +$ helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Configuration + +By default `chaoskube` runs in dry-run mode so it doesn't actually kill anything. +If you're sure you want to use it run `helm` with: + +```console +$ helm install stable/chaoskube --set dryRun=false +``` + +| Parameter | Description | Default | +|---------------------------|-----------------------------------------------------|-----------------------------------| +| `name` | container name | chaoskube | +| `image` | docker image | quay.io/linki/chaoskube | +| `imageTag` | docker image tag | v0.4.0 | +| `replicas` | number of replicas to run | 1 | +| `interval` | interval between pod terminations | 10m | +| `labels` | label selector to filter pods by | "" (matches everything) | +| `annotations` | annotation selector to filter pods by | "" (matches everything) | +| `namespaces` | namespace selector to filter pods by | "" (all namespaces) | +| `dryRun` | don't kill pods, only log what would have been done | true | +| `limitChaos` | limit chaos according to specified times/days | false | +| `location` | timezone from tz database, e.g "America/New_York" | (none) | +| `offDays` | days when chaos is to be suspended. (Or "none") | "Saturday,Sunday" | +| `chaosHrs.start` | start time for introducing chaos (24hr time) | 9:30 | +| `chaosHrs.end` | end time for introducing chaos (24hr time) | 14:30 | +| `holidays` | comma-separated, "YYYY-MM-DD" days to skip chaos | "" (none) | +| `resources.cpu` | cpu resource requests and limits | 10m | +| `resources.memory` | memory resource requests and limits | 16Mi | + +Setting label and namespaces selectors from the shell can be tricky but is possible (example with zsh): + +```console +$ helm install \ + --set labels='app=mate\,stage!=prod',namespaces='!kube-system\,!production' \ + stable/chaoskube --debug --dry-run | grep -A4 args + args: + - --in-cluster + - --interval=10m + - --labels=app=foo,stage!=prod + - --namespaces=!kube-system,!production +``` diff --git a/examples/helm/chaoskube/templates/NOTES.txt b/examples/helm/chaoskube/templates/NOTES.txt new file mode 100755 index 00000000..9912c6e6 --- /dev/null +++ b/examples/helm/chaoskube/templates/NOTES.txt @@ -0,0 +1,9 @@ +chaoskube is running and will kill arbitrary pods every {{ .Values.interval }}. + +You can follow the logs to see what chaoskube does: + + POD=$(kubectl get pods -l app={{ printf "%s-%s" .Release.Name .Values.name }} --namespace {{ .Release.Namespace }} --output name) + kubectl logs -f $POD --namespace={{ .Release.Namespace }} +{{ if .Values.dryRun }} +You are running in dry-run mode. No pod is actually terminated. +{{ end -}} diff --git a/examples/helm/chaoskube/templates/deployment.yaml b/examples/helm/chaoskube/templates/deployment.yaml new file mode 100755 index 00000000..5a7abce0 --- /dev/null +++ b/examples/helm/chaoskube/templates/deployment.yaml @@ -0,0 +1,46 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ printf "%s-%s" .Release.Name .Values.name }} + labels: + app: {{ printf "%s-%s" .Release.Name .Values.name }} + chart: {{ .Chart.Name }}-{{ .Chart.Version }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicas }} + template: + metadata: + labels: + app: {{ printf "%s-%s" .Release.Name .Values.name }} + chart: {{ .Chart.Name }}-{{ .Chart.Version }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + spec: + imagePullSecrets: + - name: {{ .Values.docker.secretName }} + containers: + - name: {{ .Values.name }} + image: {{ .Values.image }}:{{ .Values.imageTag }} + args: + - --interval={{ .Values.interval }} + - --labels={{ .Values.labels }} + - --annotations={{ .Values.annotations }} + - --namespaces={{ .Values.namespaces }} + - --location={{ .Values.location }} + - --off-days={{ .Values.offDays }} + - --holidays={{ .Values.holidays }} + - --chaos-hours=start:{{ .Values.chaosHrs.start }},end:{{ .Values.chaosHrs.end }} + {{- if not .Values.dryRun }} + - --no-dry-run + {{- end }} + {{- if .Values.limitChaos }} + - --limit-chaos + {{- end }} + resources: + requests: + cpu: {{ .Values.resources.cpu }} + memory: {{ .Values.resources.memory }} + limits: + cpu: {{ .Values.resources.cpu }} + memory: {{ .Values.resources.memory }} diff --git a/examples/helm/chaoskube/values.yaml b/examples/helm/chaoskube/values.yaml new file mode 100755 index 00000000..3c93eb03 --- /dev/null +++ b/examples/helm/chaoskube/values.yaml @@ -0,0 +1,50 @@ +# container name +name: chaoskube + +# docker image +image: quay.io/linki/chaoskube + +# docker image tag +imageTag: v0.6.1 + +# number of replicas to run +replicas: 1 + +# interval between pod terminations +interval: 10m + +# label selector to filter pods by, e.g. app=foo,stage!=prod +labels: + +# annotation selector to filter pods by, e.g. chaos.alpha.kubernetes.io/enabled=true +annotations: + +# namespace selector to filter pods by, e.g. '!kube-system,!production' (use quotes) +namespaces: + +# don't kill pods, only log what would have been done +dryRun: true + +# Limit chaos times (default: false) +limitChaos: true + +# Timezone location from the "tz database" (e.g. "America/Los_Angeles") NO DEFAULT +# Also allowed: "UTC", "Local" +location: Local + +# Comma-separated list of weekday names when chaos is suspended. Default: "Saturday, Sunday". +# (Use "none" for no off days.) +offDays: + +# Daily start and end times for introducing chaos. Default: start: 9:30, end: 16:30 +chaosHrs: + start: 9:30 + end: 13:30 + +# A list of ISO 8601 dates (YYYY-MM-DD) when chaos is suspended. Default: empty list. +holidays: + +# resource requests and limits +resources: + cpu: 10m + memory: 16Mi diff --git a/examples/chaoskube.yaml b/examples/kubernetes/chaoskube.yaml similarity index 51% rename from examples/chaoskube.yaml rename to examples/kubernetes/chaoskube.yaml index 23247d0b..d68abe36 100644 --- a/examples/chaoskube.yaml +++ b/examples/kubernetes/chaoskube.yaml @@ -14,7 +14,7 @@ spec: serviceAccountName: chaoskube containers: - name: chaoskube - image: quay.io/linki/chaoskube:v0.6.1 + image: quay.io/linki/chaoskube:v0.6.2 args: # kill a pod every 10 minutes - --interval=10m @@ -26,6 +26,19 @@ spec: - --namespaces=!kube-system # terminate pods for real: this disables dry-run mode which is on by default - --no-dry-run + # limit chaos times as specified in following options (default is off) + # --no-limit-chaos or missing options turns this off + - --limit-chaos + # timezone location from the "tz database" (e.g. "America/Los_Angeles") NO DEFAULT + # also allowed: "UTC" or "Local" + - --location=Local + # comma-separated list of weekday names when chaos is suspended. Default: "Saturday,Sunday". + # (Use "none" for no off days.) + - --off-days= + # list of ISO 8601 dates (YYYY-MM-DD) when chaos is suspended. Default: empty list. + - --holidays= + # daily start and end times for introducing chaos. Default: start:9:30,end:16:30 + - --chaos-hours= --- diff --git a/examples/rbac.yaml b/examples/kubernetes/rbac.yaml similarity index 100% rename from examples/rbac.yaml rename to examples/kubernetes/rbac.yaml diff --git a/main.go b/main.go index aa99def8..5fbd03ae 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,10 @@ package main import ( + "fmt" "os" + "strconv" + "strings" "time" log "github.com/sirupsen/logrus" @@ -15,19 +18,56 @@ import ( "github.com/linki/chaoskube/chaoskube" ) +const ( + limitChaosOpt = "limit-chaos" + locationOpt = "location" + offDaysOpt = "off-days" + chaosHrsOpt = "chaos-hours" + holidaysOpt = "holidays" + + defaultStartHr = 9 + defaultStartMin = 30 + defaultEndHr = 16 + defaultEndMin = 30 + + iso8601 = "2006-01-02" +) + var ( - labelString string - annString string - nsString string - master string - kubeconfig string - interval time.Duration - inCluster bool - dryRun bool - debug bool - version string + labelString string + annString string + nsString string + master string + kubeconfig string + interval time.Duration + inCluster bool + dryRun bool + debug bool + version string + limitChaos bool + locationString string + offDaysString string + chaosHrsString string + holidaysString string ) +// offtimeCfg holds configuration information related to when to suspend the chaos. +type offtimeCfg struct { + // Whether chaos limiting is enabled + enabled bool + // timezone in which the worktimes are expressed + location *time.Location + // Days on which chaos is suspended + offDays []time.Weekday + // Chaos start and end hours and minutes + chaosStartHr int + chaosStartMin int + chaosEndHr int + chaosEndMin int + // holidays, assumed to be expressed in UTC, regardless of Location + holidays []time.Time +} + func init() { kingpin.Flag("labels", "A set of labels to restrict the list of affected pods. Defaults to everything.").StringVar(&labelString) kingpin.Flag("annotations", "A set of annotations to restrict the list of affected pods. Defaults to everything.").StringVar(&annString) @@ -37,6 +77,14 @@ func init() { kingpin.Flag("interval", "Interval between Pod terminations").Default("10m").DurationVar(&interval) kingpin.Flag("dry-run", "If true, don't actually do anything.").Default("true").BoolVar(&dryRun) kingpin.Flag("debug", "Enable debug logging.").BoolVar(&debug) + kingpin.Flag(limitChaosOpt, "Whether to limit chaos according to configuration. Defaults to false.").Default("false").BoolVar(&limitChaos) + kingpin.Flag(locationOpt, `Timezone location from the "tz database" (e.g. "America/Los_Angeles", not "PDT") `+ + `for interpreting chaos-period start and stop times. No default.`).StringVar(&locationString) + help := fmt.Sprintf(`Daily start and end times for introducing chaos. Defaults to "start: %d:%d, end: %d:%d".`, + defaultStartHr, defaultStartMin, defaultEndHr, defaultEndMin) + kingpin.Flag(chaosHrsOpt, help).StringVar(&chaosHrsString) + kingpin.Flag(offDaysOpt, `A list of days of the week when chaos is suspended. Defaults to "Saturday, Sunday". (Use "none" for no off days.)`).StringVar(&offDaysString) + kingpin.Flag(holidaysOpt, `A list of ISO 8601 dates (YYYY-MM-DD) when chaos is suspended. Defaults to and empty list.`).StringVar(&holidaysString) } func main() { @@ -83,6 +131,18 @@ func main() { log.Infof("Filtering pods by namespaces: %s", namespaces.String()) } + offcfg, err := handleOfftimeConfig(limitChaos, locationString, offDaysString, chaosHrsString, holidaysString) + if err != nil { + log.Fatal(err) + } + if offcfg.enabled { + log.Infof("Limiting chaos. %s: %s, %s: %s, %s: %s, %s: %s", + locationOpt, locationString, + offDaysOpt, offDaysString, + chaosHrsOpt, chaosHrsString, + holidaysOpt, holidaysString) + } + chaoskube := chaoskube.New( client, labelSelector, @@ -94,8 +154,12 @@ func main() { ) for { - if err := chaoskube.TerminateVictim(); err != nil { - log.Fatal(err) + if timeToSuspend(time.Now(), *offcfg) { + log.Debugf("Chaos currently suspended") + } else { + if err := chaoskube.TerminateVictim(); err != nil { + log.Fatal(err) + } } log.Debugf("Sleeping for %s...", interval) @@ -124,3 +188,189 @@ func newClient() (*kubernetes.Clientset, error) { return client, nil } + +func setLocation(offcfg *offtimeCfg, locationStr string) error { + var err error + if len(locationStr) == 0 { + err = fmt.Errorf("timezone location is required if %s is enabled", limitChaosOpt) + return err + } + offcfg.location, err = time.LoadLocation(locationStr) + if err != nil { + err = fmt.Errorf(err.Error()+`- %s must one of: a timezone from the "tz database" (IANA), "UTC" or "Local"`, locationOpt) + return err + } + return err +} + +func setOffDays(offcfg *offtimeCfg, offDaysStr string) error { + var err error + offcfg.offDays = make([]time.Weekday, 0, 2) + if offDaysStr == "none" { + return err + } else if len(offDaysStr) == 0 { + offcfg.offDays = append(offcfg.offDays, time.Saturday, time.Sunday) + } else { + days := strings.Split(offDaysStr, ",") + for _, day := range days { + switch strings.TrimSpace(day) { + case time.Sunday.String(): + offcfg.offDays = append(offcfg.offDays, time.Sunday) + case time.Monday.String(): + offcfg.offDays = append(offcfg.offDays, time.Monday) + case time.Tuesday.String(): + offcfg.offDays = append(offcfg.offDays, time.Tuesday) + case time.Wednesday.String(): + offcfg.offDays = append(offcfg.offDays, time.Wednesday) + case time.Thursday.String(): + offcfg.offDays = append(offcfg.offDays, time.Thursday) + case time.Friday.String(): + offcfg.offDays = append(offcfg.offDays, time.Friday) + case time.Saturday.String(): + offcfg.offDays = append(offcfg.offDays, time.Saturday) + default: + err = fmt.Errorf("unrecognized day of week in %s: %s", offDaysOpt, day) + return err + } + } + } + return err +} + +func setChaosHours(offcfg *offtimeCfg, chaosHrsStr string) error { + var err error + if len(chaosHrsStr) == 0 { + offcfg.chaosStartHr = defaultStartHr + offcfg.chaosStartMin = defaultStartMin + offcfg.chaosEndHr = defaultEndHr + offcfg.chaosEndMin = defaultEndMin + } else { + startEnd := strings.Split(chaosHrsStr, ",") + for _, item := range startEnd { + switch kv := strings.SplitN(strings.TrimSpace(item), ":", 2); kv[0] { + case "start": + offcfg.chaosStartHr, offcfg.chaosStartMin, err = getHrMin(kv[1]) + if err != nil { + err = fmt.Errorf(`in %s, could not parse "%s"`, chaosHrsOpt, item) + return err + } + case "end": + offcfg.chaosEndHr, offcfg.chaosEndMin, err = getHrMin(kv[1]) + if err != nil { + err = fmt.Errorf(`in %s, could not parse "%s"`, chaosHrsOpt, item) + return err + } + default: + err = fmt.Errorf(`%s requires this format: "start: 9:30, end: 17:30". (Got key: "%s")`, chaosHrsOpt, kv[0]) + return err + } + } + } + // Validate + v1 := offcfg.chaosStartHr*10 + offcfg.chaosStartMin + v2 := offcfg.chaosEndHr*10 + offcfg.chaosEndMin + if v1 > v2 { + err = fmt.Errorf("%s may not specify a period that spans midnight, and must be expressed in 24hr time", chaosHrsOpt) + } + return err +} + +// getHrmMin parses out the hr and min from " hr:min" +func getHrMin(hrmMinStr string) (hr, min int, err error) { + hm := strings.Split(strings.TrimSpace(hrmMinStr), ":") + hr, err = strconv.Atoi(hm[0]) + if err != nil { + return hr, min, err + } + min, err = strconv.Atoi(hm[1]) + if err != nil { + return hr, min, err + } + return hr, min, err +} + +func setHolidays(offcfg *offtimeCfg, holidaysStr string) error { + var err error + if len(holidaysStr) == 0 { + // Leave Holidays nil + return err + } + offcfg.holidays = make([]time.Time, 0) + for _, hStr := range strings.Split(holidaysStr, ",") { + layout := iso8601 + var holiday time.Time + holiday, err = time.ParseInLocation(layout, strings.TrimSpace(hStr), offcfg.location) + if err != nil { + err = fmt.Errorf(`in %s, invalid date format. "YYYY-MM-DD" required. (Got "%s")`, holidaysOpt, hStr) + return err + } + offcfg.holidays = append(offcfg.holidays, holiday) + } + return err +} + +func handleOfftimeConfig(enabled bool, locationStr, offDaysStr, chaosHrsStr, holidaysStr string) (*offtimeCfg, error) { + var err error + offcfg := &offtimeCfg{} + + offcfg.enabled = enabled + if !enabled { + // Not enabled, no need to set other values + return offcfg, err + } + + if err = setLocation(offcfg, locationStr); err != nil { + return offcfg, err + } + + if err = setOffDays(offcfg, offDaysStr); err != nil { + return offcfg, err + } + + if err = setChaosHours(offcfg, chaosHrsStr); err != nil { + return offcfg, err + } + + if err = setHolidays(offcfg, holidaysStr); err != nil { + return offcfg, err + } + + return offcfg, err +} + +// timeToSuspend examines the supplied time and offtimeCfg and determines whether it is time to suspend chaos. +func timeToSuspend(currTime time.Time, offcfg offtimeCfg) bool { + if !offcfg.enabled { + // If limiting not enabled, it's never time to suspend + return false + } + + // Localize the currTime + locTime := currTime.In(offcfg.location) + + // Check offDays + currDay := locTime.Weekday() + for _, od := range offcfg.offDays { + if currDay == od { + return true + } + } + + // Check holidays + ty, tm, td := locTime.Date() + for _, holiday := range offcfg.holidays { + hy, hm, hd := holiday.Date() + if ty == hy && tm == hm && td == hd { + return true + } + } + + // Check time of day. Start by getting today's chaos start/end times + chaosStart := time.Date(ty, tm, td, offcfg.chaosStartHr, offcfg.chaosStartMin, 0, 0, offcfg.location) + chaosEnd := time.Date(ty, tm, td, offcfg.chaosEndHr, offcfg.chaosEndMin, 0, 0, offcfg.location) + if !((chaosStart.Before(locTime) || chaosStart.Equal(locTime)) && locTime.Before(chaosEnd)) { + return true + } + + return false +} diff --git a/main_test.go b/main_test.go new file mode 100644 index 00000000..f4dbbf40 --- /dev/null +++ b/main_test.go @@ -0,0 +1,504 @@ +package main + +import ( + "fmt" + "strings" + "testing" + "time" +) + +const dhmsLayout = "2006-01-02 15:04:05" + +func Test_handleOfftimeConfig_01(t *testing.T) { + descr := "Zero value locationStr" + var locationStr string + var offDaysStr string + var chaosHrsStr string + var holidaysStr string + _, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err == nil { + t.Errorf("%s: Expected err", descr) + } else { + substr := "required" + msg := err.Error() + if !strings.Contains(msg, substr) { + t.Errorf(`%s: Expected "%s" in error message. Got "%s"`, descr, substr, msg) + } + } +} + +func Test_handleOfftimeConfig_02(t *testing.T) { + descr := "Unparsable locationStr" + var locationStr = "PDT" + var offDaysStr string + var chaosHrsStr string + var holidaysStr string + _, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err == nil { + t.Errorf("%s: Expected err", descr) + } else { + substr := "tz database" + msg := err.Error() + if !strings.Contains(msg, substr) { + t.Errorf(`%s: Expected "%s" in error message. Got "%s"`, descr, substr, msg) + } + } +} + +func Test_handleOfftimeConfig_03(t *testing.T) { + descr := "Good timezone locationStr" + var locationStr = "America/Los_Angeles" + var offDaysStr string + var chaosHrsStr string + var holidaysStr string + offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf("%s: Unexpected err: %s", descr, err.Error()) + } else { + actual := offcfg.location.String() + if actual != locationStr { + t.Errorf(`"%s: offcfg.location: Expected "%s", got "%s"`, descr, locationStr, actual) + } + } +} + +func Test_handleOfftimeConfig_04(t *testing.T) { + descr := `Unset/Empty offDays` + var locationStr = "UTC" + var offDaysStr string + var chaosHrsStr string + var holidaysStr string + offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf("%s: Unexpected err: %s", descr, err.Error()) + } else { + expected := []time.Weekday{time.Saturday, time.Sunday} + actual := offcfg.offDays + if !wkdSlicesEquivalent(expected, actual) { + t.Errorf(`%s: "offcfg.offDays: Expected "%#v", got "%#v"`, descr, expected, actual) + } + } + // Test empty string for offDaysStr + offDaysStr = "" + offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf("%s: Unexpected err: %s", descr, err.Error()) + } else { + expected := []time.Weekday{time.Saturday, time.Sunday} + actual := offcfg.offDays + if !wkdSlicesEquivalent(expected, actual) { + t.Errorf(`%s: "offcfg.offDays: Expected "%#v", got "%#v"`, descr, expected, actual) + } + } +} + +func Test_handleOfftimeConfig_05(t *testing.T) { + descr := `offDays = "none"` + var locationStr = "UTC" + var offDaysStr = "none" + var chaosHrsStr string + var holidaysStr string + offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf("%s: Unexpected err: %s", descr, err.Error()) + } else { + var expected []time.Weekday + actual := offcfg.offDays + if !wkdSlicesEquivalent(expected, actual) { + t.Errorf(`%s: "offcfg.offDays: Expected "%#v", got "%#v"`, descr, expected, actual) + } + } + // Test empty string for offDaysStr + offDaysStr = "" + offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf("%s: Unexpected err: %s", descr, err.Error()) + } else { + expected := []time.Weekday{time.Saturday, time.Sunday} + actual := offcfg.offDays + if !wkdSlicesEquivalent(expected, actual) { + t.Errorf(`%s: "offcfg.offDays: Expected "%#v", got "%#v"`, descr, expected, actual) + } + } +} + +func Test_handleOfftimeConfig_06(t *testing.T) { + descr := `Bad offDays` + var locationStr = "UTC" + var offDaysStr = "Saturday, Zontag" + var chaosHrsStr string + var holidaysStr string + _, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err == nil { + t.Errorf("%s: Expected err", descr) + } else { + substr := "unrecognized" + msg := err.Error() + if !strings.Contains(msg, substr) { + t.Errorf(`%s: Expected "%s" in error message. Got "%s"`, descr, substr, msg) + } + } +} + +func Test_handleOfftimeConfig_07(t *testing.T) { + descr := `Three off days` + var locationStr = "UTC" + var offDaysStr = "Thursday, Monday,Friday" // various whitespace too + var chaosHrsStr string + var holidaysStr string + offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf("%s: Unexpected err: %s", descr, err.Error()) + } else { + expected := []time.Weekday{time.Monday, time.Thursday, time.Friday} + actual := offcfg.offDays + if !wkdSlicesEquivalent(expected, actual) { + t.Errorf(`%s: "offcfg.offDays: Expected "%#v", got "%#v"`, descr, expected, actual) + } + } +} + +func Test_handleOfftimeConfig_08(t *testing.T) { + descr := `Empty chaos hours` + var locationStr = "UTC" + var offDaysStr string + var chaosHrsStr string + var holidaysStr string + offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf("%s: Unexpected err: %s", descr, err.Error()) + } else { + loc, _ := time.LoadLocation(locationStr) + expected := offtimeCfg{ + enabled: true, + location: loc, + offDays: []time.Weekday{time.Saturday, time.Sunday}, + chaosStartHr: defaultStartHr, + chaosStartMin: defaultStartMin, + chaosEndHr: defaultEndHr, + chaosEndMin: defaultEndMin, + } + actual := offcfg + // Just compare the start/end times + if !(actual.chaosStartHr == expected.chaosStartHr && + actual.chaosStartMin == expected.chaosStartMin && + actual.chaosEndHr == expected.chaosEndHr && + actual.chaosEndMin == expected.chaosEndMin) { + t.Errorf(`%s: Expected "%#v", got "%#v"`, descr, expected, actual) + } + } +} + +func Test_handleOfftimeConfig_09(t *testing.T) { + descr := `chaos hours with leading zeros should be OK` + var locationStr = "UTC" + var offDaysStr string + var chaosHrsStr = "start: 00:01, end: 03:00" + var holidaysStr string + offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf("%s: Unexpected err: %s", descr, err.Error()) + } else { + loc, _ := time.LoadLocation(locationStr) + expected := offtimeCfg{ + enabled: true, + location: loc, + offDays: []time.Weekday{time.Saturday, time.Sunday}, + chaosStartHr: 0, + chaosStartMin: 1, + chaosEndHr: 3, + chaosEndMin: 0, + } + actual := offcfg + // Just compare the start/end times + if !(actual.chaosStartHr == expected.chaosStartHr && + actual.chaosStartMin == expected.chaosStartMin && + actual.chaosEndHr == expected.chaosEndHr && + actual.chaosEndMin == expected.chaosEndMin) { + t.Errorf(`%s: Expected "%#v", got "%#v"`, descr, expected, actual) + } + } +} + +func Test_handleOfftimeConfig_10(t *testing.T) { + descr := `chaos hours may not span midnight` + var locationStr = "UTC" + var offDaysStr string + var chaosHrsStr = "start: 11:59, end: 00:00" + var holidaysStr string + _, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err == nil { + t.Errorf("%s: Expected error.", descr) + } else { + substr := "midnight" + msg := err.Error() + if !strings.Contains(msg, substr) { + t.Errorf(`%s: Expected "%s" in error message. Got "%s"`, descr, substr, msg) + } + } +} + +func Test_handleOfftimeConfig_11(t *testing.T) { + descr := `chaos hours - bad number` + var locationStr = "UTC" + var offDaysStr string + var chaosHrsStr = "start: 1O:59, end: 13:00" // capital O (letter) not 0 + var holidaysStr string + _, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err == nil { + t.Errorf("%s: Expected error.", descr) + } else { + substr := "could not parse" + msg := err.Error() + if !strings.Contains(msg, substr) { + t.Errorf(`%s: Expected "%s" in error message. Got "%s"`, descr, substr, msg) + } + } +} + +func Test_handleOfftimeConfig_12(t *testing.T) { + descr := `Empty holidays should be OK` + var locationStr = "UTC" + var offDaysStr string + var chaosHrsStr string + var holidaysStr string + offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf("%s: Unexpected err: %s", descr, err.Error()) + } else { + actual := len(offcfg.holidays) + if actual != 0 { + t.Errorf(`%s: Expected 0 holidays, got %d`, descr, actual) + } + } +} + +func Test_handleOfftimeConfig_13(t *testing.T) { + descr := `One holiday: New Years Day in Los Angeles` + var locationStr = "America/Los_Angeles" + var offDaysStr string + var chaosHrsStr string + var holidaysStr = "2016-01-01" + offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf("%s: Unexpected err: %s", descr, err.Error()) + } else { + actual := len(offcfg.holidays) + if actual != 1 { + t.Errorf(`%s: Expected 1 holiday, got %d`, descr, actual) + } + dateStr := offcfg.holidays[0].Format(time.RFC822Z) + expected := "01 Jan 16 00:00 -0800" + if dateStr != expected { + t.Errorf(`%s: Expected "%s", got "%s"`, descr, expected, dateStr) + } + } +} + +func Test_handleOfftimeConfig_14(t *testing.T) { + descr := `Two holidays in Los Angeles` + var locationStr = "America/Los_Angeles" + var offDaysStr string + var chaosHrsStr string + var holidaysStr = "2016-01-01, 2014-12-25" + offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf("%s: Unexpected err: %s", descr, err.Error()) + } else { + actual := len(offcfg.holidays) + if actual != 2 { + t.Errorf(`%s: Expected 2 holidays, got %d`, descr, actual) + } + + dateStr := offcfg.holidays[0].Format(time.RFC822Z) + expected := "01 Jan 16 00:00 -0800" + if dateStr != expected { + t.Errorf(`%s: Expected "%s", got "%s"`, descr, expected, dateStr) + } + + dateStr = offcfg.holidays[1].Format(time.RFC822Z) + expected = "25 Dec 14 00:00 -0800" + if dateStr != expected { + t.Errorf(`%s: Expected "%s", got "%s"`, descr, expected, dateStr) + } + } +} + +func Test_handleOfftimeConfig_15(t *testing.T) { + descr := `Bad holiday string` + var locationStr = "UTC" + var offDaysStr string + var chaosHrsStr string + var holidaysStr = "1/1/2016" + _, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err == nil { + t.Errorf("%s: Expected err", descr) + } else { + substr := "invalid date format" + msg := err.Error() + if !strings.Contains(msg, substr) { + t.Errorf(`%s: Expected "%s" in error message. Got "%s"`, descr, substr, msg) + } + } +} + +func Test_timeToSuspend_01(t *testing.T) { + descr := "Suspend - respect stated timezone" + var locationStr = "America/Los_Angeles" + var offDaysStr = "none" + var chaosHrsStr string + var holidaysStr = "2016-01-01" + offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf(`%s: ERROR IN TEST`, descr) + } + utc, err := time.LoadLocation("UTC") + if err != nil { + t.Errorf(`%s: ERROR IN TEST`, descr) + } + + // Set testTime to same day as holiday, but in UTC + testTime, err := time.ParseInLocation(iso8601, holidaysStr, utc) + if err != nil { + t.Errorf(`%s: ERROR IN TEST`, descr) + } + + // Date is correct, current time is UTC, so Los Angeles is still day before + actual := timeToSuspend(testTime, *offcfg) + expected := false + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } + + // Now use first instant of holiday in Los Angeles TZ + testTime, err = time.ParseInLocation(iso8601, holidaysStr, offcfg.location) + if err != nil { + t.Errorf(`%s: ERROR IN TEST`, descr) + } + actual = timeToSuspend(testTime, *offcfg) + expected = true + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } +} + +func Test_timeToSuspend_02(t *testing.T) { + descr := "Suspend - respect off days" + var locationStr = "America/Los_Angeles" + var offDaysStr = "none" + var chaosHrsStr string + var holidaysStr string + offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf(`%s: ERROR IN TEST`, descr) + } + + var testDayStr = "2016-01-05 13:00:00" // this is a Tuesday + + testTime, err := time.ParseInLocation(dhmsLayout, testDayStr, offcfg.location) + if err != nil { + t.Errorf(`%s: ERROR IN TEST`, descr) + } + + // No holidays, no off days, default chaosHours + actual := timeToSuspend(testTime, *offcfg) + expected := false + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } + + // Set offDays to Wednesday + offDaysStr = "Wednesday" + offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf(`%s: ERROR IN TEST`, descr) + } + actual = timeToSuspend(testTime, *offcfg) + expected = false + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } + + // Set offDays to Tuesday + offDaysStr = "Tuesday" + offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf(`%s: ERROR IN TEST`, descr) + } + actual = timeToSuspend(testTime, *offcfg) + expected = true + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } +} + +func Test_timeToSuspend_03(t *testing.T) { + descr := "Suspend - respect chaos hrs" + var locationStr = "America/Los_Angeles" + var offDaysStr = "none" + var chaosHrsStr string // use defaults + var holidaysStr string + offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + if err != nil { + t.Errorf(`%s: ERROR IN TEST`, descr) + } + + var testTimeStr = fmt.Sprintf("2016-01-05 %d:%d:00", offcfg.chaosStartHr, offcfg.chaosStartMin) + testTime, err := time.ParseInLocation(dhmsLayout, testTimeStr, offcfg.location) + if err != nil { + t.Errorf(`%s: ERROR IN TEST`, descr) + } + + // No holidays, no off days, default chaosHours - testTime is start of chaos period + actual := timeToSuspend(testTime, *offcfg) + expected := false + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } + + // Subtract to one sec before chaos period + minusOneSec, err := time.ParseDuration("-1s") + if err != nil { + t.Errorf(`%s: ERROR IN TEST`, descr) + } + testTime = testTime.Add(minusOneSec) + actual = timeToSuspend(testTime, *offcfg) + expected = true + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } + + // Set to end of chaos period + testTimeStr = fmt.Sprintf("2016-01-05 %d:%d:00", offcfg.chaosEndHr, offcfg.chaosEndMin) + testTime, err = time.ParseInLocation(dhmsLayout, testTimeStr, offcfg.location) + if err != nil { + t.Errorf(`%s: ERROR IN TEST`, descr) + } + actual = timeToSuspend(testTime, *offcfg) + expected = true + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } +} + +// ----------------------------------------------------------------------------- + +// wkdSlicesEquivalent compares two slices of time.Weekday but ignores order. +func wkdSlicesEquivalent(a []time.Weekday, b []time.Weekday) bool { + if len(a) != len(b) { + return false + } + for _, aw := range a { + found := false + for _, bw := range b { + if bw == aw { + found = true + break + } + } + if !found { + return false + } + } + return true +} From 86af649321ded92d71add9faeb5743c09d967c08 Mon Sep 17 00:00:00 2001 From: Tonnis Wildeboer Date: Fri, 8 Dec 2017 14:16:34 -0800 Subject: [PATCH 2/4] Clean up tests --- main_test.go | 438 +++++++++++++++++++++++++++------------------------ 1 file changed, 229 insertions(+), 209 deletions(-) diff --git a/main_test.go b/main_test.go index f4dbbf40..c95d3689 100644 --- a/main_test.go +++ b/main_test.go @@ -9,13 +9,21 @@ import ( const dhmsLayout = "2006-01-02 15:04:05" -func Test_handleOfftimeConfig_01(t *testing.T) { - descr := "Zero value locationStr" +func Test_handleOfftimeConfig_location(t *testing.T) { + var err error + var descr string var locationStr string var offDaysStr string var chaosHrsStr string var holidaysStr string - _, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + var offcfg *offtimeCfg + + descr = "Zero value locationStr" + locationStr = "" + offDaysStr = "" + chaosHrsStr = "" + holidaysStr = "" + _, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err == nil { t.Errorf("%s: Expected err", descr) } else { @@ -25,15 +33,15 @@ func Test_handleOfftimeConfig_01(t *testing.T) { t.Errorf(`%s: Expected "%s" in error message. Got "%s"`, descr, substr, msg) } } -} -func Test_handleOfftimeConfig_02(t *testing.T) { - descr := "Unparsable locationStr" - var locationStr = "PDT" - var offDaysStr string - var chaosHrsStr string - var holidaysStr string - _, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + // ------------------------------------------------------------------------- + + descr = "Unparsable locationStr" + locationStr = "PDT" + offDaysStr = "" + chaosHrsStr = "" + holidaysStr = "" + _, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err == nil { t.Errorf("%s: Expected err", descr) } else { @@ -43,15 +51,15 @@ func Test_handleOfftimeConfig_02(t *testing.T) { t.Errorf(`%s: Expected "%s" in error message. Got "%s"`, descr, substr, msg) } } -} -func Test_handleOfftimeConfig_03(t *testing.T) { - descr := "Good timezone locationStr" - var locationStr = "America/Los_Angeles" - var offDaysStr string - var chaosHrsStr string - var holidaysStr string - offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + // ------------------------------------------------------------------------- + + descr = "Good timezone locationStr" + locationStr = "America/Los_Angeles" + offDaysStr = "" + chaosHrsStr = "" + holidaysStr = "" + offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err != nil { t.Errorf("%s: Unexpected err: %s", descr, err.Error()) } else { @@ -62,24 +70,20 @@ func Test_handleOfftimeConfig_03(t *testing.T) { } } -func Test_handleOfftimeConfig_04(t *testing.T) { - descr := `Unset/Empty offDays` - var locationStr = "UTC" +func Test_handleOfftimeConfig_offDays(t *testing.T) { + var err error + var descr string + var locationStr string var offDaysStr string var chaosHrsStr string var holidaysStr string - offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) - if err != nil { - t.Errorf("%s: Unexpected err: %s", descr, err.Error()) - } else { - expected := []time.Weekday{time.Saturday, time.Sunday} - actual := offcfg.offDays - if !wkdSlicesEquivalent(expected, actual) { - t.Errorf(`%s: "offcfg.offDays: Expected "%#v", got "%#v"`, descr, expected, actual) - } - } - // Test empty string for offDaysStr + var offcfg *offtimeCfg + + descr = `Empty offDays` + locationStr = "UTC" offDaysStr = "" + chaosHrsStr = "" + holidaysStr = "" offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err != nil { t.Errorf("%s: Unexpected err: %s", descr, err.Error()) @@ -90,45 +94,31 @@ func Test_handleOfftimeConfig_04(t *testing.T) { t.Errorf(`%s: "offcfg.offDays: Expected "%#v", got "%#v"`, descr, expected, actual) } } -} -func Test_handleOfftimeConfig_05(t *testing.T) { - descr := `offDays = "none"` - var locationStr = "UTC" - var offDaysStr = "none" - var chaosHrsStr string - var holidaysStr string - offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) - if err != nil { - t.Errorf("%s: Unexpected err: %s", descr, err.Error()) - } else { - var expected []time.Weekday - actual := offcfg.offDays - if !wkdSlicesEquivalent(expected, actual) { - t.Errorf(`%s: "offcfg.offDays: Expected "%#v", got "%#v"`, descr, expected, actual) - } - } - // Test empty string for offDaysStr - offDaysStr = "" + // ------------------------------------------------------------------------- + + descr = `offDays = "none"` + locationStr = "UTC" + offDaysStr = "none" offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err != nil { t.Errorf("%s: Unexpected err: %s", descr, err.Error()) } else { - expected := []time.Weekday{time.Saturday, time.Sunday} + var expected []time.Weekday actual := offcfg.offDays if !wkdSlicesEquivalent(expected, actual) { t.Errorf(`%s: "offcfg.offDays: Expected "%#v", got "%#v"`, descr, expected, actual) } } -} -func Test_handleOfftimeConfig_06(t *testing.T) { - descr := `Bad offDays` - var locationStr = "UTC" - var offDaysStr = "Saturday, Zontag" - var chaosHrsStr string - var holidaysStr string - _, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + // ------------------------------------------------------------------------- + + descr = `Bad offDays` + locationStr = "UTC" + offDaysStr = "Saturday, Zontag" + chaosHrsStr = "" + holidaysStr = "" + _, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err == nil { t.Errorf("%s: Expected err", descr) } else { @@ -138,15 +128,15 @@ func Test_handleOfftimeConfig_06(t *testing.T) { t.Errorf(`%s: Expected "%s" in error message. Got "%s"`, descr, substr, msg) } } -} -func Test_handleOfftimeConfig_07(t *testing.T) { - descr := `Three off days` - var locationStr = "UTC" - var offDaysStr = "Thursday, Monday,Friday" // various whitespace too - var chaosHrsStr string - var holidaysStr string - offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + // ------------------------------------------------------------------------- + + descr = `Three off days` + locationStr = "UTC" + offDaysStr = "Thursday, Monday,Friday" // various whitespace too + chaosHrsStr = "" + holidaysStr = "" + offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err != nil { t.Errorf("%s: Unexpected err: %s", descr, err.Error()) } else { @@ -158,13 +148,21 @@ func Test_handleOfftimeConfig_07(t *testing.T) { } } -func Test_handleOfftimeConfig_08(t *testing.T) { - descr := `Empty chaos hours` - var locationStr = "UTC" +func Test_handleOfftimeConfig_chaosHrs(t *testing.T) { + var err error + var descr string + var locationStr string var offDaysStr string var chaosHrsStr string var holidaysStr string - offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + var offcfg *offtimeCfg + + descr = `Empty chaos hours` + locationStr = "UTC" + offDaysStr = "" + chaosHrsStr = "" + holidaysStr = "" + offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err != nil { t.Errorf("%s: Unexpected err: %s", descr, err.Error()) } else { @@ -187,15 +185,15 @@ func Test_handleOfftimeConfig_08(t *testing.T) { t.Errorf(`%s: Expected "%#v", got "%#v"`, descr, expected, actual) } } -} -func Test_handleOfftimeConfig_09(t *testing.T) { - descr := `chaos hours with leading zeros should be OK` - var locationStr = "UTC" - var offDaysStr string - var chaosHrsStr = "start: 00:01, end: 03:00" - var holidaysStr string - offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + // ------------------------------------------------------------------------- + + descr = `chaos hours with leading zeros should be OK` + locationStr = "UTC" + offDaysStr = "" + chaosHrsStr = "start: 00:01, end: 03:00" + holidaysStr = "" + offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err != nil { t.Errorf("%s: Unexpected err: %s", descr, err.Error()) } else { @@ -218,15 +216,15 @@ func Test_handleOfftimeConfig_09(t *testing.T) { t.Errorf(`%s: Expected "%#v", got "%#v"`, descr, expected, actual) } } -} -func Test_handleOfftimeConfig_10(t *testing.T) { - descr := `chaos hours may not span midnight` - var locationStr = "UTC" - var offDaysStr string - var chaosHrsStr = "start: 11:59, end: 00:00" - var holidaysStr string - _, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + // ------------------------------------------------------------------------- + + descr = `chaos hours may not span midnight` + locationStr = "UTC" + offDaysStr = "" + chaosHrsStr = "start: 11:59, end: 00:00" + holidaysStr = "" + _, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err == nil { t.Errorf("%s: Expected error.", descr) } else { @@ -236,15 +234,15 @@ func Test_handleOfftimeConfig_10(t *testing.T) { t.Errorf(`%s: Expected "%s" in error message. Got "%s"`, descr, substr, msg) } } -} -func Test_handleOfftimeConfig_11(t *testing.T) { - descr := `chaos hours - bad number` - var locationStr = "UTC" - var offDaysStr string - var chaosHrsStr = "start: 1O:59, end: 13:00" // capital O (letter) not 0 - var holidaysStr string - _, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + // ------------------------------------------------------------------------- + + descr = `chaos hours - bad number` + locationStr = "UTC" + offDaysStr = "" + chaosHrsStr = "start: 1O:59, end: 13:00" // capital O (letter) not 0 + holidaysStr = "" + _, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err == nil { t.Errorf("%s: Expected error.", descr) } else { @@ -256,13 +254,20 @@ func Test_handleOfftimeConfig_11(t *testing.T) { } } -func Test_handleOfftimeConfig_12(t *testing.T) { - descr := `Empty holidays should be OK` - var locationStr = "UTC" +func Test_handleOfftimeConfig_holidays(t *testing.T) { + var err error + var descr string + var locationStr string var offDaysStr string var chaosHrsStr string var holidaysStr string - offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + var offcfg *offtimeCfg + + locationStr = "UTC" + offDaysStr = "" + chaosHrsStr = "" + holidaysStr = "" + offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err != nil { t.Errorf("%s: Unexpected err: %s", descr, err.Error()) } else { @@ -271,15 +276,15 @@ func Test_handleOfftimeConfig_12(t *testing.T) { t.Errorf(`%s: Expected 0 holidays, got %d`, descr, actual) } } -} -func Test_handleOfftimeConfig_13(t *testing.T) { - descr := `One holiday: New Years Day in Los Angeles` - var locationStr = "America/Los_Angeles" - var offDaysStr string - var chaosHrsStr string - var holidaysStr = "2016-01-01" - offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + // ------------------------------------------------------------------------- + + descr = `One holiday: New Years Day in Los Angeles` + locationStr = "America/Los_Angeles" + offDaysStr = "" + chaosHrsStr = "" + holidaysStr = "2016-01-01" + offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err != nil { t.Errorf("%s: Unexpected err: %s", descr, err.Error()) } else { @@ -293,15 +298,15 @@ func Test_handleOfftimeConfig_13(t *testing.T) { t.Errorf(`%s: Expected "%s", got "%s"`, descr, expected, dateStr) } } -} -func Test_handleOfftimeConfig_14(t *testing.T) { - descr := `Two holidays in Los Angeles` - var locationStr = "America/Los_Angeles" - var offDaysStr string - var chaosHrsStr string - var holidaysStr = "2016-01-01, 2014-12-25" - offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + // ------------------------------------------------------------------------- + + descr = `Two holidays in Los Angeles` + locationStr = "America/Los_Angeles" + offDaysStr = "" + chaosHrsStr = "" + holidaysStr = "2016-01-01, 2014-12-25" + offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err != nil { t.Errorf("%s: Unexpected err: %s", descr, err.Error()) } else { @@ -322,15 +327,15 @@ func Test_handleOfftimeConfig_14(t *testing.T) { t.Errorf(`%s: Expected "%s", got "%s"`, descr, expected, dateStr) } } -} -func Test_handleOfftimeConfig_15(t *testing.T) { - descr := `Bad holiday string` - var locationStr = "UTC" - var offDaysStr string - var chaosHrsStr string - var holidaysStr = "1/1/2016" - _, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + // ------------------------------------------------------------------------- + + descr = `Bad holiday string` + locationStr = "UTC" + offDaysStr = "" + chaosHrsStr = "" + holidaysStr = "1/1/2016" + _, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err == nil { t.Errorf("%s: Expected err", descr) } else { @@ -342,142 +347,157 @@ func Test_handleOfftimeConfig_15(t *testing.T) { } } -func Test_timeToSuspend_01(t *testing.T) { - descr := "Suspend - respect stated timezone" - var locationStr = "America/Los_Angeles" - var offDaysStr = "none" +func Test_timeToSuspend(t *testing.T) { + var err error + var descr string + var locationStr string + var offDaysStr string var chaosHrsStr string - var holidaysStr = "2016-01-01" - offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + var holidaysStr string + var offcfg *offtimeCfg + var testTime time.Time + + descr = "Suspend - respect stated timezone" + locationStr = "America/Los_Angeles" + offDaysStr = "none" + chaosHrsStr = "" + holidaysStr = "2016-01-01" + offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err != nil { - t.Errorf(`%s: ERROR IN TEST`, descr) + t.Errorf(`%s: ERROR IN TEST - %v`, descr, err) + return } utc, err := time.LoadLocation("UTC") if err != nil { - t.Errorf(`%s: ERROR IN TEST`, descr) + t.Errorf(`%s: ERROR IN TEST - %v`, descr, err) + return } // Set testTime to same day as holiday, but in UTC - testTime, err := time.ParseInLocation(iso8601, holidaysStr, utc) + testTime, err = time.ParseInLocation(iso8601, holidaysStr, utc) if err != nil { - t.Errorf(`%s: ERROR IN TEST`, descr) - } - - // Date is correct, current time is UTC, so Los Angeles is still day before - actual := timeToSuspend(testTime, *offcfg) - expected := false - if actual != expected { - t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + t.Errorf(`%s: ERROR IN TEST - %v`, descr, err) + } else { + // Date is correct, current time is UTC, so Los Angeles is still day before + actual := timeToSuspend(testTime, *offcfg) + expected := false + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } } // Now use first instant of holiday in Los Angeles TZ testTime, err = time.ParseInLocation(iso8601, holidaysStr, offcfg.location) if err != nil { - t.Errorf(`%s: ERROR IN TEST`, descr) - } - actual = timeToSuspend(testTime, *offcfg) - expected = true - if actual != expected { - t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + t.Errorf(`%s: ERROR IN TEST - %v`, descr, err) + } else { + actual := timeToSuspend(testTime, *offcfg) + expected := true + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } } -} -func Test_timeToSuspend_02(t *testing.T) { - descr := "Suspend - respect off days" - var locationStr = "America/Los_Angeles" - var offDaysStr = "none" - var chaosHrsStr string - var holidaysStr string - offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + // ------------------------------------------------------------------------- + + descr = "Suspend - respect off days" + locationStr = "America/Los_Angeles" + offDaysStr = "none" + offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err != nil { - t.Errorf(`%s: ERROR IN TEST`, descr) + t.Errorf(`%s: ERROR IN TEST - %v`, descr, err) } var testDayStr = "2016-01-05 13:00:00" // this is a Tuesday - testTime, err := time.ParseInLocation(dhmsLayout, testDayStr, offcfg.location) + testTime, err = time.ParseInLocation(dhmsLayout, testDayStr, offcfg.location) if err != nil { - t.Errorf(`%s: ERROR IN TEST`, descr) - } - - // No holidays, no off days, default chaosHours - actual := timeToSuspend(testTime, *offcfg) - expected := false - if actual != expected { - t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + t.Errorf(`%s: ERROR IN TEST - %v`, descr, err) + } else { + // No holidays, no off days, default chaosHours + actual := timeToSuspend(testTime, *offcfg) + expected := false + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } } // Set offDays to Wednesday offDaysStr = "Wednesday" offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err != nil { - t.Errorf(`%s: ERROR IN TEST`, descr) - } - actual = timeToSuspend(testTime, *offcfg) - expected = false - if actual != expected { - t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + t.Errorf(`%s: ERROR IN TEST - %v`, descr, err) + } else { + actual := timeToSuspend(testTime, *offcfg) + expected := false + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } } // Set offDays to Tuesday offDaysStr = "Tuesday" offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err != nil { - t.Errorf(`%s: ERROR IN TEST`, descr) - } - actual = timeToSuspend(testTime, *offcfg) - expected = true - if actual != expected { - t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + t.Errorf(`%s: ERROR IN TEST - %v`, descr, err) + } else { + actual := timeToSuspend(testTime, *offcfg) + expected := true + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } } -} -func Test_timeToSuspend_03(t *testing.T) { - descr := "Suspend - respect chaos hrs" - var locationStr = "America/Los_Angeles" - var offDaysStr = "none" - var chaosHrsStr string // use defaults - var holidaysStr string - offcfg, err := handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) + // ------------------------------------------------------------------------- + + descr = "Suspend - respect chaos hrs" + locationStr = "America/Los_Angeles" + offDaysStr = "none" + chaosHrsStr = "" // use defaults + holidaysStr = "" + offcfg, err = handleOfftimeConfig(true, locationStr, offDaysStr, chaosHrsStr, holidaysStr) if err != nil { - t.Errorf(`%s: ERROR IN TEST`, descr) + t.Errorf(`%s: ERROR IN TEST - %v`, descr, err) + return } var testTimeStr = fmt.Sprintf("2016-01-05 %d:%d:00", offcfg.chaosStartHr, offcfg.chaosStartMin) - testTime, err := time.ParseInLocation(dhmsLayout, testTimeStr, offcfg.location) + testTime, err = time.ParseInLocation(dhmsLayout, testTimeStr, offcfg.location) if err != nil { - t.Errorf(`%s: ERROR IN TEST`, descr) - } - - // No holidays, no off days, default chaosHours - testTime is start of chaos period - actual := timeToSuspend(testTime, *offcfg) - expected := false - if actual != expected { - t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + t.Errorf(`%s: ERROR IN TEST - %v`, descr, err) + } else { + // No holidays, no off days, default chaosHours - testTime is start of chaos period + actual := timeToSuspend(testTime, *offcfg) + expected := false + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } } // Subtract to one sec before chaos period minusOneSec, err := time.ParseDuration("-1s") if err != nil { - t.Errorf(`%s: ERROR IN TEST`, descr) - } - testTime = testTime.Add(minusOneSec) - actual = timeToSuspend(testTime, *offcfg) - expected = true - if actual != expected { - t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + t.Errorf(`%s: ERROR IN TEST - %v`, descr, err) + } else { + testTime = testTime.Add(minusOneSec) + actual := timeToSuspend(testTime, *offcfg) + expected := true + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } } // Set to end of chaos period testTimeStr = fmt.Sprintf("2016-01-05 %d:%d:00", offcfg.chaosEndHr, offcfg.chaosEndMin) testTime, err = time.ParseInLocation(dhmsLayout, testTimeStr, offcfg.location) if err != nil { - t.Errorf(`%s: ERROR IN TEST`, descr) - } - actual = timeToSuspend(testTime, *offcfg) - expected = true - if actual != expected { - t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + t.Errorf(`%s: ERROR IN TEST - %v`, descr, err) + } else { + actual := timeToSuspend(testTime, *offcfg) + expected := true + if actual != expected { + t.Errorf(`%s: Got %v, expected %v`, descr, actual, expected) + } } } From 47c855c2cddcc54de9038abf16764b4b8f3c607d Mon Sep 17 00:00:00 2001 From: Tonnis Wildeboer Date: Tue, 12 Dec 2017 11:41:51 -0800 Subject: [PATCH 3/4] Remove unneeded reference to imagePullSecret --- examples/helm/chaoskube/templates/deployment.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/helm/chaoskube/templates/deployment.yaml b/examples/helm/chaoskube/templates/deployment.yaml index 5a7abce0..bbc0070c 100755 --- a/examples/helm/chaoskube/templates/deployment.yaml +++ b/examples/helm/chaoskube/templates/deployment.yaml @@ -17,8 +17,6 @@ spec: release: {{ .Release.Name }} heritage: {{ .Release.Service }} spec: - imagePullSecrets: - - name: {{ .Values.docker.secretName }} containers: - name: {{ .Values.name }} image: {{ .Values.image }}:{{ .Values.imageTag }} From 2f7820d814a16fc1e66dcce21b17b55c3445321f Mon Sep 17 00:00:00 2001 From: Tonnis Wildeboer Date: Tue, 12 Dec 2017 11:44:04 -0800 Subject: [PATCH 4/4] Use presumed image id in chart example. --- examples/helm/chaoskube/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/helm/chaoskube/values.yaml b/examples/helm/chaoskube/values.yaml index 3c93eb03..3a4ed796 100755 --- a/examples/helm/chaoskube/values.yaml +++ b/examples/helm/chaoskube/values.yaml @@ -5,7 +5,7 @@ name: chaoskube image: quay.io/linki/chaoskube # docker image tag -imageTag: v0.6.1 +imageTag: v0.6.2 # number of replicas to run replicas: 1