├── .gitignore ├── artifacts └── kubes │ └── scaling │ ├── chart │ ├── Chart.yaml │ ├── values-prod.yaml │ ├── .helmignore │ ├── values.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── deployment.yaml │ │ ├── rbac.yaml │ │ ├── _helpers.tpl │ │ └── crd.yaml │ └── README.md │ ├── scaler.yml │ ├── hpa.yml │ ├── deployment.yml │ └── crd.yml ├── test ├── scaling │ └── controller_test.go └── external │ ├── gen-mocks.go │ ├── autoscalingv1.go │ ├── corev1.go │ └── kubernetes.go ├── pkg ├── client │ ├── listers │ │ └── scaling │ │ │ └── v1alpha1 │ │ │ ├── gen-mocks.go │ │ │ ├── expansion_generated.go │ │ │ ├── scheduledscaler.go │ │ │ └── mock_v1alpha1 │ │ │ └── scheduledscaler.go │ ├── clientset │ │ └── versioned │ │ │ ├── doc.go │ │ │ ├── typed │ │ │ └── scaling │ │ │ │ └── v1alpha1 │ │ │ │ ├── fake │ │ │ │ ├── doc.go │ │ │ │ ├── fake_scaling_client.go │ │ │ │ └── fake_scheduledscaler.go │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── mock_v1alpha1 │ │ │ │ ├── scaling_client.go │ │ │ │ └── scheduledscaler.go │ │ │ │ ├── scaling_client.go │ │ │ │ └── scheduledscaler.go │ │ │ ├── scheme │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ ├── mock_versioned │ │ │ └── clientset.go │ │ │ └── clientset.go │ └── informers │ │ └── externalversions │ │ ├── internalinterfaces │ │ └── factory_interfaces.go │ │ ├── scaling │ │ ├── v1alpha1 │ │ │ ├── interface.go │ │ │ └── scheduledscaler.go │ │ └── interface.go │ │ ├── generic.go │ │ └── factory.go ├── services │ └── scaling │ │ ├── step │ │ └── parser.go │ │ ├── metadata │ │ └── cluster.go │ │ └── cron │ │ ├── proxy.go │ │ ├── proxy_test.go │ │ └── mock_cron │ │ └── proxy.go ├── apis │ └── scaling │ │ ├── register.go │ │ └── v1alpha1 │ │ ├── doc.go │ │ ├── register.go │ │ ├── types.go │ │ └── zz_generated.deepcopy.go └── signals │ ├── signal_windows.go │ ├── signal_posix.go │ └── signal.go ├── Dockerfile ├── .travis.yml ├── hack ├── print_helm_values.py └── update-codegen.sh ├── Makefile ├── .all-contributorsrc ├── go.mod ├── README.md ├── scaling-controller_test.go ├── LICENSE └── scaling-controller.go /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | bin 3 | -------------------------------------------------------------------------------- /artifacts/kubes/scaling/chart/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: "1.0" 3 | description: Scheduled Scaler Helm Chart 4 | name: scheduled-scaler 5 | version: 0.0.4 6 | -------------------------------------------------------------------------------- /artifacts/kubes/scaling/chart/values-prod.yaml: -------------------------------------------------------------------------------- 1 | # image: 2 | # tag: master 3 | resources: 4 | limits: 5 | cpu: 200m 6 | memory: 50Mi 7 | requests: 8 | cpu: 200m 9 | memory: 50Mi 10 | -------------------------------------------------------------------------------- /test/scaling/controller_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNeverFail(t *testing.T) { 10 | assert.False(t, false, "Should never fail!") 11 | } 12 | -------------------------------------------------------------------------------- /pkg/client/listers/scaling/v1alpha1/gen-mocks.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | //go:generate mockgen -destination=mock_$GOPACKAGE/scheduledscaler.go k8s.restdev.com/operators/pkg/client/listers/scaling/v1alpha1 ScheduledScalerLister,ScheduledScalerNamespaceLister 4 | -------------------------------------------------------------------------------- /artifacts/kubes/scaling/scaler.yml: -------------------------------------------------------------------------------- 1 | apiVersion: "scaling.k8s.restdev.com/v1alpha1" 2 | kind: ScheduledScaler 3 | metadata: 4 | name: my-scheduled-scaler-1 5 | spec: 6 | target: 7 | kind: HorizontalPodAutoscaler 8 | name: my-hpa-1 9 | apiVersion: autoscaling/v1 10 | steps: 11 | #run at 5:30 UTC 12 | - runat: '0 30 5 * * *' 13 | mode: range 14 | minReplicas: 1 15 | maxReplicas: 5 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest as alpine 2 | RUN apk --no-cache add tzdata zip ca-certificates 3 | WORKDIR /usr/share/zoneinfo 4 | # -0 means no compression. Needed because go's 5 | # tz loader doesn't handle compressed data. 6 | RUN zip -r -0 /zoneinfo.zip . 7 | 8 | FROM scratch 9 | 10 | ENV ZONEINFO /zoneinfo.zip 11 | COPY --from=alpine /zoneinfo.zip / 12 | 13 | ARG bin 14 | ENV bin ${bin} 15 | 16 | COPY ${bin} /${bin} 17 | -------------------------------------------------------------------------------- /artifacts/kubes/scaling/hpa.yml: -------------------------------------------------------------------------------- 1 | apiVersion: autoscaling/v2beta1 2 | kind: HorizontalPodAutoscaler 3 | metadata: 4 | name: my-hpa-1 5 | namespace: test 6 | spec: 7 | scaleTargetRef: 8 | apiVersion: apps/v1beta1 9 | kind: Deployment 10 | name: test 11 | minReplicas: 1 12 | maxReplicas: 10 13 | metrics: 14 | - type: Resource 15 | resource: 16 | name: cpu 17 | targetAverageUtilization: 50 18 | -------------------------------------------------------------------------------- /pkg/services/scaling/step/parser.go: -------------------------------------------------------------------------------- 1 | package step 2 | 3 | import scalingv1alpha1 "k8s.restdev.com/operators/pkg/apis/scaling/v1alpha1" 4 | 5 | func Parse(step scalingv1alpha1.ScheduledScalerStep) (min, max *int32) { 6 | if step.Mode == "range" { 7 | min = step.MinReplicas 8 | max = step.MaxReplicas 9 | } 10 | 11 | if step.Mode == "fixed" { 12 | min = step.Replicas 13 | max = step.Replicas 14 | } 15 | 16 | return 17 | } 18 | -------------------------------------------------------------------------------- /artifacts/kubes/scaling/chart/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /test/external/gen-mocks.go: -------------------------------------------------------------------------------- 1 | package mock_external 2 | 3 | //go:generate mockgen -destination=kubernetes.go -package mock_external k8s.io/client-go/kubernetes Interface 4 | //go:generate mockgen -destination=corev1.go -package mock_external k8s.io/client-go/kubernetes/typed/core/v1 CoreV1Interface,ConfigMapInterface 5 | //go:generate mockgen -destination=autoscalingv1.go -package mock_external k8s.io/client-go/kubernetes/typed/autoscaling/v1 AutoscalingV1Interface,HorizontalPodAutoscalerInterface 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.18.x 4 | go_import_path: k8s.restdev.com/operators 5 | services: 6 | - docker 7 | jobs: 8 | include: 9 | - stage: Testing 10 | script: make test OPERATOR=scaling 11 | - stage: Dockerizing 12 | if: tag =~ ^(test-)?[0-9]+.[0-9]+.[0-9]+$ 13 | script: 14 | - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin 15 | - make build OPERATOR="scaling" VERSION="$TRAVIS_TAG" 16 | - make push OPERATOR="scaling" VERSION="$TRAVIS_TAG" 17 | -------------------------------------------------------------------------------- /hack/print_helm_values.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import yaml 3 | 4 | print_format="| {parameter:<40}| | {default:<50}|" 5 | def walk_dict(d,keys=[],depth=0): 6 | for k,v in sorted(d.items(),key=lambda x: x[0]): 7 | keys.append(k) 8 | if isinstance(v,dict): 9 | walk_dict(v,keys,depth+1) 10 | else: 11 | print(print_format.format(parameter='`{0}`'.format(".".join(keys)),default='`{0}`'.format(v))) 12 | keys.pop() 13 | 14 | s = open("./values.yaml") 15 | d = yaml.load(s) 16 | 17 | walk_dict(d) 18 | -------------------------------------------------------------------------------- /pkg/apis/scaling/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | package scaling 15 | 16 | const ( 17 | GroupName = "scaling.k8s.restdev.com" 18 | ) 19 | -------------------------------------------------------------------------------- /artifacts/kubes/scaling/chart/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for scheduled-scaler. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: k8srestdev/scaling 9 | tag: 0.0.4 10 | pullPolicy: Always 11 | sslCerts: 12 | hostPath: "/etc/ssl/certs" 13 | resources: {} 14 | # If you do want to specify resources, uncomment the following 15 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 16 | # limits: 17 | # cpu: 100m 18 | # memory: 128Mi 19 | # requests: 20 | # cpu: 100m 21 | # memory: 128Mi 22 | 23 | rbac: 24 | create: true 25 | 26 | nodeSelector: {} 27 | 28 | tolerations: [] 29 | 30 | affinity: {} 31 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // This package has the automatically generated clientset. 18 | package versioned 19 | -------------------------------------------------------------------------------- /pkg/signals/signal_windows.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | package signals 15 | 16 | import ( 17 | "os" 18 | ) 19 | 20 | var shutdownSignals = []os.Signal{os.Interrupt} 21 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/scaling/v1alpha1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package fake has the automatically generated clients. 18 | package fake 19 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // This package contains the scheme of the automatically generated clientset. 18 | package scheme 19 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/scaling/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // This package has the automatically generated typed clients. 18 | package v1alpha1 19 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/scaling/v1alpha1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | type ScheduledScalerExpansion interface{} 20 | -------------------------------------------------------------------------------- /pkg/apis/scaling/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | // +k8s:deepcopy-gen=package 15 | 16 | // Package v1alpha1 is the v1alpha1 version of the API. 17 | // +groupName=scaling.k8s.restdev.com 18 | package v1alpha1 19 | -------------------------------------------------------------------------------- /pkg/signals/signal_posix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | /* 4 | Copyright 2017 The Kubernetes Authors. 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package signals 17 | 18 | import ( 19 | "os" 20 | "syscall" 21 | ) 22 | 23 | var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM} 24 | -------------------------------------------------------------------------------- /artifacts/kubes/scaling/deployment.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: "extensions/v1beta1" 3 | kind: "Deployment" 4 | metadata: 5 | name: "k8s-restdev-scaling" 6 | namespace: "kube-system" 7 | spec: 8 | strategy: 9 | rollingUpdate: 10 | maxSurge: 80% 11 | maxUnavailable: 50% 12 | type: RollingUpdate 13 | template: 14 | metadata: 15 | labels: 16 | name: "k8s-restdev-scaling" 17 | spec: 18 | containers: 19 | - 20 | image: "[IMAGE]" 21 | name: "k8s-restdev-scaling" 22 | command: ["/bin/k8s-restdev-scaling"] 23 | volumeMounts: 24 | - name: ssl-certs 25 | mountPath: /etc/ssl/certs 26 | volumes: 27 | - name: ssl-certs 28 | hostPath: 29 | path: /etc/ssl/certs 30 | -------------------------------------------------------------------------------- /artifacts/kubes/scaling/chart/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. scheduled-scaler controller deployed. 2 | 3 | Check the scheduled-scaler logs 4 | export POD=$(kubectl get pods -l app={{ template "scheduled-scaler.fullname" . }} --namespace {{ .Release.Namespace }} --output name) 5 | kubectl logs $POD --namespace={{ .Release.Namespace }} 6 | 7 | 2. Create scheduled-scalers targeting HPA or IG, for example: 8 | 9 | apiVersion: "scaling.k8s.restdev.com/v1alpha1" 10 | kind: ScheduledScaler 11 | metadata: 12 | name: my-test 13 | spec: 14 | timeZone: Asia/Singapore 15 | target: 16 | kind: HorizontalPodAutoscaler 17 | name: test 18 | apiVersion: autoscaling/v1 19 | steps: 20 | #run at 13:05 GMT+8 21 | - runat: '0 5 13 * * *' 22 | mode: range 23 | minReplicas: 3 24 | maxReplicas: 5 25 | -------------------------------------------------------------------------------- /pkg/services/scaling/metadata/cluster.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | import ( 4 | "strings" 5 | 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | func GetClusterInfo() (projectId, zone string, err error) { 11 | httpclient := &http.Client{} 12 | projectIdReq, err := http.NewRequest("GET", "http://metadata.google.internal/computeMetadata/v1/project/project-id", nil) 13 | projectIdReq.Header.Add("Metadata-Flavor", "Google") 14 | projectIdResp, err := httpclient.Do(projectIdReq) 15 | zoneReq, err := http.NewRequest("GET", "http://metadata.google.internal/computeMetadata/v1/instance/zone", nil) 16 | zoneReq.Header.Add("Metadata-Flavor", "Google") 17 | zoneResp, err := httpclient.Do(zoneReq) 18 | defer zoneResp.Body.Close() 19 | defer projectIdResp.Body.Close() 20 | projectIdBody, err := ioutil.ReadAll(projectIdResp.Body) 21 | projectId = string(projectIdBody) 22 | zoneBody, err := ioutil.ReadAll(zoneResp.Body) 23 | zoneSlice := strings.Split(string(zoneBody), "/") 24 | zone = zoneSlice[len(zoneSlice)-1] 25 | 26 | return projectId, zone, err 27 | } 28 | -------------------------------------------------------------------------------- /pkg/client/listers/scaling/v1alpha1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // This file was automatically generated by lister-gen 18 | 19 | package v1alpha1 20 | 21 | // ScheduledScalerListerExpansion allows custom methods to be added to 22 | // ScheduledScalerLister. 23 | type ScheduledScalerListerExpansion interface{} 24 | 25 | // ScheduledScalerNamespaceListerExpansion allows custom methods to be added to 26 | // ScheduledScalerNamespaceLister. 27 | type ScheduledScalerNamespaceListerExpansion interface{} 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DATE:=$(shell date +%s) 2 | 3 | GOBIN=go 4 | GOBUILD=$(GOBIN) build 5 | GOTEST=$(GOBIN) test 6 | 7 | OPERATOR?=scaling 8 | CONTROLLER=$(OPERATOR)-controller.go 9 | TEST_CONTROLLER=$(OPERATOR)-controller_test.go 10 | BIN=bin/k8s-restdev-$(OPERATOR) 11 | 12 | DOCKERBIN=docker 13 | VERSION?=kube-system.$(DATE) 14 | IMAGE?=k8srestdev/$(OPERATOR):$(VERSION) 15 | DOCKERBUILD=$(DOCKERBIN) build --build-arg bin=$(BIN) -t $(IMAGE) . 16 | 17 | DEPLOYBIN?=kubectl 18 | KN_PROJECT_ID?=$(PROJECT_ID) 19 | 20 | .PHONY: test 21 | 22 | all: test build push deploy 23 | test: 24 | $(GOTEST) $(TEST_CONTROLLER) $(CONTROLLER) 25 | $(GOTEST) ./... 26 | build: 27 | GOOS=linux $(GOBUILD) \ 28 | -a --ldflags '-extldflags "-static"' \ 29 | -tags netgo \ 30 | -installsuffix netgo \ 31 | -v \ 32 | -o $(BIN) $(CONTROLLER) 33 | $(DOCKERBUILD) 34 | push: 35 | docker push $(IMAGE) 36 | deploy: 37 | ifeq ($(DEPLOYBIN), kn) 38 | cat ./artifacts/kubes/$(OPERATOR)/deployment.yml | sed "s|\[IMAGE\]|$(IMAGE)|g" | kn $(KN_PROJECT_ID) -- --namespace=kube-system apply -f - 39 | else 40 | cat ./artifacts/kubes/$(OPERATOR)/deployment.yml | sed "s|\[IMAGE\]|$(IMAGE)|g" | kubectl --namespace=kube-system apply -f - 41 | endif 42 | -------------------------------------------------------------------------------- /pkg/signals/signal.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | package signals 15 | 16 | import ( 17 | "os" 18 | "os/signal" 19 | ) 20 | 21 | var onlyOneSignalHandler = make(chan struct{}) 22 | 23 | // SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned 24 | // which is closed on one of these signals. If a second signal is caught, the program 25 | // is terminated with exit code 1. 26 | func SetupSignalHandler() (stopCh <-chan struct{}) { 27 | close(onlyOneSignalHandler) // panics when called twice 28 | 29 | stop := make(chan struct{}) 30 | c := make(chan os.Signal, 2) 31 | signal.Notify(c, shutdownSignals...) 32 | go func() { 33 | <-c 34 | close(stop) 35 | <-c 36 | os.Exit(1) // second signal. Exit directly. 37 | }() 38 | 39 | return stop 40 | } 41 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/scaling/v1alpha1/fake/fake_scaling_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package fake 18 | 19 | import ( 20 | rest "k8s.io/client-go/rest" 21 | testing "k8s.io/client-go/testing" 22 | v1alpha1 "k8s.restdev.com/operators/pkg/client/clientset/versioned/typed/scaling/v1alpha1" 23 | ) 24 | 25 | type FakeScalingV1alpha1 struct { 26 | *testing.Fake 27 | } 28 | 29 | func (c *FakeScalingV1alpha1) ScheduledScalers(namespace string) v1alpha1.ScheduledScalerInterface { 30 | return &FakeScheduledScalers{c, namespace} 31 | } 32 | 33 | // RESTClient returns a RESTClient that is used to communicate 34 | // with API server by this client implementation. 35 | func (c *FakeScalingV1alpha1) RESTClient() rest.Interface { 36 | var ret *rest.RESTClient 37 | return ret 38 | } 39 | -------------------------------------------------------------------------------- /artifacts/kubes/scaling/chart/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ template "scheduled-scaler.fullname" . }} 5 | labels: {{ include "scheduled-scaler.labels" . | indent 4 }} 6 | spec: 7 | replicas: {{ .Values.replicaCount }} 8 | selector: 9 | matchLabels: 10 | app: {{ template "scheduled-scaler.name" . }} 11 | release: {{ .Release.Name }} 12 | template: 13 | metadata: 14 | labels: {{ include "scheduled-scaler.labels" . | indent 8 }} 15 | spec: 16 | {{- if .Values.rbac.create }} 17 | serviceAccount: {{ template "scheduled-scaler.fullname" . }} 18 | {{- end }} 19 | volumes: 20 | - name: ssl-certs 21 | hostPath: 22 | path: {{ .Values.sslCerts.hostPath }} 23 | containers: 24 | - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" 25 | name: {{ .Chart.Name }} 26 | command: ["/bin/k8s-restdev-scaling"] 27 | volumeMounts: 28 | - name: ssl-certs 29 | mountPath: /etc/ssl/certs 30 | resources: 31 | {{ toYaml .Values.resources | indent 12 }} 32 | {{- with .Values.nodeSelector }} 33 | nodeSelector: 34 | {{ toYaml . | indent 8 }} 35 | {{- end }} 36 | {{- with .Values.affinity }} 37 | affinity: 38 | {{ toYaml . | indent 8 }} 39 | {{- end }} 40 | {{- with .Values.tolerations }} 41 | tolerations: 42 | {{ toYaml . | indent 8 }} 43 | {{- end }} 44 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // This file was automatically generated by informer-gen 18 | 19 | package internalinterfaces 20 | 21 | import ( 22 | time "time" 23 | 24 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | runtime "k8s.io/apimachinery/pkg/runtime" 26 | cache "k8s.io/client-go/tools/cache" 27 | versioned "k8s.restdev.com/operators/pkg/client/clientset/versioned" 28 | ) 29 | 30 | type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer 31 | 32 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 33 | type SharedInformerFactory interface { 34 | Start(stopCh <-chan struct{}) 35 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 36 | } 37 | 38 | type TweakListOptionsFunc func(*v1.ListOptions) 39 | -------------------------------------------------------------------------------- /pkg/services/scaling/cron/proxy.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | //go:generate mockgen -source $GOFILE -destination=mock_$GOPACKAGE/$GOFILE -package mock_$GOPACKAGE 4 | 5 | import ( 6 | "time" 7 | 8 | "github.com/robfig/cron" 9 | ) 10 | 11 | // CronProxy wraps the cron object for testing purposes, as this interface can be mocked. 12 | type CronProxy interface { 13 | Parse(spec string) (cron.Schedule, error) 14 | Create(timeZone string) (*cron.Cron, error) 15 | Push(c *cron.Cron, time string, call func()) 16 | Start(c *cron.Cron) 17 | Stop(c *cron.Cron) 18 | } 19 | 20 | // CronImpl passes methods through to cron methods. 21 | type CronImpl struct { 22 | } 23 | 24 | // Parse parses the spec into a schedule. 25 | func (ci *CronImpl) Parse(spec string) (cron.Schedule, error) { 26 | return cron.Parse(spec) 27 | } 28 | 29 | // Create creates a cron object for the given timeZone. 30 | func (ci *CronImpl) Create(timeZone string) (*cron.Cron, error) { 31 | l, err := time.LoadLocation(timeZone) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | return cron.NewWithLocation(l), nil 37 | } 38 | 39 | // Push pushes the time spec onto the cron, c, with call callback. 40 | func (ci *CronImpl) Push(c *cron.Cron, time string, call func()) { 41 | s, _ := cron.Parse(time) 42 | c.Schedule(s, cron.FuncJob(call)) 43 | } 44 | 45 | // Start starts the cron object, c. 46 | func (ci *CronImpl) Start(c *cron.Cron) { 47 | c.Start() 48 | } 49 | 50 | // Stop stops the cron object, c. 51 | func (ci *CronImpl) Stop(c *cron.Cron) { 52 | c.Stop() 53 | } 54 | -------------------------------------------------------------------------------- /artifacts/kubes/scaling/chart/templates/rbac.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.rbac.create -}} 2 | kind: ClusterRole 3 | apiVersion: rbac.authorization.k8s.io/v1 4 | metadata: 5 | name: {{ template "scheduled-scaler.fullname" . }} 6 | rules: 7 | # Allow scheduled-scaler controller full control on its CRD's group 8 | - apiGroups: ["scaling.k8s.restdev.com"] 9 | resources: ["*"] 10 | verbs: ["*"] 11 | nonResourceURLs: [] 12 | # Allow scheduled-scaler controller to list, get, update hpa in non-core APIs 13 | - apiGroups: ["autoscaling"] 14 | resources: ["horizontalpodautoscalers", "horizontalpodautoscalers/status"] 15 | verbs: ["list", "get", "update"] 16 | nonResourceURLs: [] 17 | # Allow scheduled-scaler controller to list, get, update hpa in extensions API 18 | - apiGroups: ["extensions"] 19 | resources: ["horizontalpodautoscalers", "horizontalpodautoscalers/status"] 20 | verbs: ["list", "get", "update"] 21 | nonResourceURLs: [] 22 | --- 23 | apiVersion: v1 24 | kind: ServiceAccount 25 | metadata: 26 | name: {{ template "scheduled-scaler.fullname" . }} 27 | namespace: {{ .Release.Namespace }} 28 | --- 29 | apiVersion: rbac.authorization.k8s.io/v1 30 | kind: ClusterRoleBinding 31 | metadata: 32 | name: {{ template "scheduled-scaler.fullname" . }} 33 | roleRef: 34 | apiGroup: rbac.authorization.k8s.io 35 | kind: ClusterRole 36 | name: {{ template "scheduled-scaler.fullname" . }} 37 | subjects: 38 | - kind: ServiceAccount 39 | name: {{ template "scheduled-scaler.fullname" . }} 40 | namespace: {{ .Release.Namespace }} 41 | {{- end -}} 42 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "scr-oath", 10 | "name": "Sheridan C Rawlins", 11 | "avatar_url": "https://avatars.githubusercontent.com/u/41922797?v=4", 12 | "profile": "https://github.com/scr-oath", 13 | "contributions": [ 14 | "maintenance", 15 | "test", 16 | "code" 17 | ] 18 | }, 19 | { 20 | "login": "vnandha", 21 | "name": "vnandha", 22 | "avatar_url": "https://avatars.githubusercontent.com/u/10261643?v=4", 23 | "profile": "https://github.com/vnandha", 24 | "contributions": [ 25 | "bug", 26 | "maintenance" 27 | ] 28 | }, 29 | { 30 | "login": "so0k", 31 | "name": "so0k", 32 | "avatar_url": "https://avatars.githubusercontent.com/u/1762599?v=4", 33 | "profile": "https://twitter.com/vincentdesmet", 34 | "contributions": [ 35 | "infra", 36 | "doc" 37 | ] 38 | }, 39 | { 40 | "login": "andwun", 41 | "name": "Andreas Wunderlich", 42 | "avatar_url": "https://avatars.githubusercontent.com/u/252058?v=4", 43 | "profile": "https://andwun.me/", 44 | "contributions": [ 45 | "doc" 46 | ] 47 | } 48 | ], 49 | "contributorsPerLine": 7, 50 | "projectName": "scheduled-scaler", 51 | "projectOwner": "West-Coast-Devops", 52 | "repoType": "github", 53 | "repoHost": "https://github.com", 54 | "skipCi": true 55 | } 56 | -------------------------------------------------------------------------------- /pkg/services/scaling/cron/proxy_test.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "github.com/stretchr/testify/require" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestCronImpl_Create(t *testing.T) { 11 | 12 | // Set up test expectation objects in outer testing scope. 13 | losAngelesTZ, err := time.LoadLocation("America/Los_Angeles") 14 | require.NoError(t, err) 15 | assert.Equal(t, "America/Los_Angeles", losAngelesTZ.String()) 16 | newYorkTZ, err := time.LoadLocation("America/New_York") 17 | require.NoError(t, err) 18 | assert.NotEqual(t, losAngelesTZ, newYorkTZ) 19 | assert.Equal(t, "America/New_York", newYorkTZ.String()) 20 | 21 | tests := []struct { 22 | name string 23 | timeZone string 24 | wantLocation *time.Location 25 | wantErr bool 26 | }{ 27 | { 28 | name: `empty is UTC`, 29 | wantLocation: time.UTC, 30 | }, 31 | { 32 | name: `Local is Local`, 33 | timeZone: "Local", 34 | wantLocation: time.Local, 35 | }, 36 | { 37 | name: `bogus timezone returns error`, 38 | timeZone: "bogus", 39 | wantErr: true, 40 | }, 41 | { 42 | name: `PST`, 43 | timeZone: "America/Los_Angeles", 44 | wantLocation: losAngelesTZ, 45 | }, 46 | { 47 | name: `EST`, 48 | timeZone: "America/New_York", 49 | wantLocation: newYorkTZ, 50 | }, 51 | } 52 | for _, tt := range tests { 53 | t.Run(tt.name, func(t *testing.T) { 54 | ci := &CronImpl{} 55 | got, err := ci.Create(tt.timeZone) 56 | if tt.wantErr { 57 | require.Error(t, err) 58 | return 59 | } 60 | require.NoError(t, err) 61 | require.NotNil(t, got) 62 | assert.Equal(t, tt.wantLocation, got.Location()) 63 | }) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /hack/update-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2017 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | RED='\033[0;31m' 22 | GREEN='\033[0;32m' 23 | NC='\033[0m' 24 | 25 | SCRIPT_ROOT=$(dirname ${BASH_SOURCE})/.. 26 | CODEGEN_PKG="../../k8s.io/code-generator" 27 | MODULE_NAME="k8s.restdev.com/operators" 28 | MODULE_PATH="${GOPATH}/src/${MODULE_NAME}" 29 | if [ $PWD != $MODULE_PATH ]; then 30 | echo "Invalid module path! Please refer to the documentation..." 31 | echo -e "${RED}Current Path:${NC} $PWD" 32 | echo -e "${GREEN}Required Path:${NC} $MODULE_PATH" 33 | exit 1 34 | fi 35 | 36 | # generate the code with: 37 | # --output-base because this script should also be able to run inside the vendor dir of 38 | # k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir 39 | # instead of the $GOPATH directly. For normal projects this can be dropped. 40 | ${CODEGEN_PKG}/generate-groups.sh "deepcopy,client,informer,lister" \ 41 | ${MODULE_NAME}/pkg/client ${MODULE_NAME}/pkg/apis \ 42 | scaling:v1alpha1 43 | 44 | # To use your own boilerplate text append: 45 | # --go-header-file ${SCRIPT_ROOT}/hack/custom-boilerplate.go.txt 46 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/scaling/v1alpha1/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // This file was automatically generated by informer-gen 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | internalinterfaces "k8s.restdev.com/operators/pkg/client/informers/externalversions/internalinterfaces" 23 | ) 24 | 25 | // Interface provides access to all the informers in this group version. 26 | type Interface interface { 27 | // ScheduledScalers returns a ScheduledScalerInformer. 28 | ScheduledScalers() ScheduledScalerInformer 29 | } 30 | 31 | type version struct { 32 | factory internalinterfaces.SharedInformerFactory 33 | namespace string 34 | tweakListOptions internalinterfaces.TweakListOptionsFunc 35 | } 36 | 37 | // New returns a new Interface. 38 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 39 | return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 40 | } 41 | 42 | // ScheduledScalers returns a ScheduledScalerInformer. 43 | func (v *version) ScheduledScalers() ScheduledScalerInformer { 44 | return &scheduledScalerInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 45 | } 46 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/scaling/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // This file was automatically generated by informer-gen 18 | 19 | package scaling 20 | 21 | import ( 22 | internalinterfaces "k8s.restdev.com/operators/pkg/client/informers/externalversions/internalinterfaces" 23 | v1alpha1 "k8s.restdev.com/operators/pkg/client/informers/externalversions/scaling/v1alpha1" 24 | ) 25 | 26 | // Interface provides access to each of this group's versions. 27 | type Interface interface { 28 | // V1alpha1 provides access to shared informers for resources in V1alpha1. 29 | V1alpha1() v1alpha1.Interface 30 | } 31 | 32 | type group struct { 33 | factory internalinterfaces.SharedInformerFactory 34 | namespace string 35 | tweakListOptions internalinterfaces.TweakListOptionsFunc 36 | } 37 | 38 | // New returns a new Interface. 39 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 40 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 41 | } 42 | 43 | // V1alpha1 returns a new v1alpha1.Interface. 44 | func (g *group) V1alpha1() v1alpha1.Interface { 45 | return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/apis/scaling/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | package v1alpha1 15 | 16 | import ( 17 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 | "k8s.io/apimachinery/pkg/runtime" 19 | "k8s.io/apimachinery/pkg/runtime/schema" 20 | 21 | scaling "k8s.restdev.com/operators/pkg/apis/scaling" 22 | ) 23 | 24 | // SchemeGroupVersion is group version used to register these objects 25 | var SchemeGroupVersion = schema.GroupVersion{Group: scaling.GroupName, Version: "v1alpha1"} 26 | 27 | // Kind takes an unqualified kind and returns back a Group qualified GroupKind 28 | func Kind(kind string) schema.GroupKind { 29 | return SchemeGroupVersion.WithKind(kind).GroupKind() 30 | } 31 | 32 | // Resource takes an unqualified resource and returns a Group qualified GroupResource 33 | func Resource(resource string) schema.GroupResource { 34 | return SchemeGroupVersion.WithResource(resource).GroupResource() 35 | } 36 | 37 | var ( 38 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 39 | AddToScheme = SchemeBuilder.AddToScheme 40 | ) 41 | 42 | // Adds the list of known types to Scheme. 43 | func addKnownTypes(scheme *runtime.Scheme) error { 44 | scheme.AddKnownTypes(SchemeGroupVersion, 45 | &ScheduledScaler{}, 46 | &ScheduledScalerList{}, 47 | ) 48 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /artifacts/kubes/scaling/chart/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "scheduled-scaler.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "scheduled-scaler.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "scheduled-scaler.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | 34 | 35 | {{- /* 36 | scheduled-scaler.labels prints the standard Helm labels. 37 | The standard labels are frequently used in metadata. 38 | */ -}} 39 | {{- define "scheduled-scaler.labels" }} 40 | app: {{ template "scheduled-scaler.name" . }} 41 | chart: {{ template "scheduled-scaler.chartref" . }} 42 | heritage: {{ .Release.Service | quote }} 43 | release: {{ .Release.Name | quote }} 44 | {{- end }} 45 | 46 | {{- /* 47 | scheduled-scaler.chartref prints a chart name and version. 48 | It does minimal escaping for use in Kubernetes labels. 49 | Example output: 50 | zookeeper-1.2.3 51 | wordpress-3.2.1_20170219 52 | */ -}} 53 | {{- define "scheduled-scaler.chartref" -}} 54 | {{- replace "+" "_" .Chart.Version | printf "%s-%s" .Chart.Name -}} 55 | {{- end -}} -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/scheme/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package scheme 18 | 19 | import ( 20 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | runtime "k8s.io/apimachinery/pkg/runtime" 22 | schema "k8s.io/apimachinery/pkg/runtime/schema" 23 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 24 | scalingv1alpha1 "k8s.restdev.com/operators/pkg/apis/scaling/v1alpha1" 25 | ) 26 | 27 | var Scheme = runtime.NewScheme() 28 | var Codecs = serializer.NewCodecFactory(Scheme) 29 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 30 | 31 | func init() { 32 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 33 | AddToScheme(Scheme) 34 | } 35 | 36 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 37 | // of clientsets, like in: 38 | // 39 | // import ( 40 | // "k8s.io/client-go/kubernetes" 41 | // clientsetscheme "k8s.io/client-go/kuberentes/scheme" 42 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 43 | // ) 44 | // 45 | // kclientset, _ := kubernetes.NewForConfig(c) 46 | // aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 47 | // 48 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 49 | // correctly. 50 | func AddToScheme(scheme *runtime.Scheme) { 51 | scalingv1alpha1.AddToScheme(scheme) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/apis/scaling/v1alpha1/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | package v1alpha1 15 | 16 | import ( 17 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 | ) 19 | 20 | // +genclient 21 | // +genclient:noStatus 22 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 23 | 24 | type ScheduledScaler struct { 25 | metav1.TypeMeta `json:",inline"` 26 | metav1.ObjectMeta `json:"metadata,omitempty"` 27 | 28 | Spec ScheduledScalerSpec `json:"spec"` 29 | Status ScheduledScalerStatus `json:"status"` 30 | } 31 | 32 | type ScheduledScalerTarget struct { 33 | Kind string `json:"kind"` 34 | ApiVersion string `json:"apiVersion"` 35 | Name string `json:"name"` 36 | } 37 | 38 | type ScheduledScalerStep struct { 39 | Runat string `json:"runat"` 40 | Mode string `json:"mode"` 41 | MinReplicas *int32 `json:"minReplicas"` 42 | MaxReplicas *int32 `json:"maxReplicas"` 43 | Replicas *int32 `json:"replicas"` 44 | } 45 | 46 | type ScheduledScalerSpec struct { 47 | Target ScheduledScalerTarget `json:"target"` 48 | Steps []ScheduledScalerStep `json:"steps"` 49 | TimeZone string `json:"timeZone"` 50 | } 51 | 52 | // FooStatus is the status for a Foo resource 53 | type ScheduledScalerStatus struct { 54 | Mode string `json:"mode"` 55 | MinReplicas int32 `json:"minReplicas"` 56 | MaxReplicas int32 `json:"maxReplicas"` 57 | } 58 | 59 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 60 | 61 | type ScheduledScalerList struct { 62 | metav1.TypeMeta `json:",inline"` 63 | metav1.ListMeta `json:"metadata"` 64 | 65 | Items []ScheduledScaler `json:"items"` 66 | } 67 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module k8s.restdev.com/operators 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/PuerkitoBio/purell v1.0.0 // indirect 7 | github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2 // indirect 8 | github.com/emicklei/go-restful v1.1.4-0.20170410110728-ff4f55a20633 // indirect 9 | github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1 // indirect 10 | github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9 // indirect 11 | github.com/go-openapi/spec v0.0.0-20170914061247-7abd5745472f // indirect 12 | github.com/go-openapi/swag v0.0.0-20170606142751-f3f9494671f9 // indirect 13 | github.com/gogo/protobuf v0.0.0-20170330071051-c0656edd0d9e // indirect 14 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b 15 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect 16 | github.com/golang/mock v1.6.0 17 | github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367 // indirect 18 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d // indirect 19 | github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7 // indirect 20 | github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c // indirect 21 | github.com/imdario/mergo v0.0.0-20141206190957-6633656539c1 // indirect 22 | github.com/json-iterator/go v0.0.0-20170829155851-36b14963da70 // indirect 23 | github.com/juju/ratelimit v0.0.0-20170523012141-5b9ff8664717 // indirect 24 | github.com/mailru/easyjson v0.0.0-20170624190925-2f5df55504eb // indirect 25 | github.com/peterbourgon/diskv v2.0.1+incompatible // indirect 26 | github.com/robfig/cron v1.0.1-0.20171101201047-2315d5715e36 27 | github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff // indirect 28 | github.com/stretchr/testify v1.7.0 29 | go.uber.org/multierr v1.5.0 30 | golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 31 | golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 32 | google.golang.org/api v0.79.0 33 | google.golang.org/appengine v1.6.7 // indirect 34 | gopkg.in/inf.v0 v0.9.0 // indirect 35 | gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 // indirect 36 | k8s.io/api v0.0.0-20171207041203-11147472b7c9 37 | k8s.io/apimachinery v0.0.0-20171207040834-180eddb345a5 38 | k8s.io/client-go v6.0.0+incompatible 39 | k8s.io/kube-openapi v0.0.0-20171101183504-39a7bf85c140 // indirect 40 | ) 41 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/generic.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // This file was automatically generated by informer-gen 18 | 19 | package externalversions 20 | 21 | import ( 22 | "fmt" 23 | 24 | schema "k8s.io/apimachinery/pkg/runtime/schema" 25 | cache "k8s.io/client-go/tools/cache" 26 | v1alpha1 "k8s.restdev.com/operators/pkg/apis/scaling/v1alpha1" 27 | ) 28 | 29 | // GenericInformer is type of SharedIndexInformer which will locate and delegate to other 30 | // sharedInformers based on type 31 | type GenericInformer interface { 32 | Informer() cache.SharedIndexInformer 33 | Lister() cache.GenericLister 34 | } 35 | 36 | type genericInformer struct { 37 | informer cache.SharedIndexInformer 38 | resource schema.GroupResource 39 | } 40 | 41 | // Informer returns the SharedIndexInformer. 42 | func (f *genericInformer) Informer() cache.SharedIndexInformer { 43 | return f.informer 44 | } 45 | 46 | // Lister returns the GenericLister. 47 | func (f *genericInformer) Lister() cache.GenericLister { 48 | return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) 49 | } 50 | 51 | // ForResource gives generic access to a shared informer of the matching type 52 | // TODO extend this to unknown resources with a client pool 53 | func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { 54 | switch resource { 55 | // Group=scaling.k8s.restdev.com, Version=v1alpha1 56 | case v1alpha1.SchemeGroupVersion.WithResource("scheduledscalers"): 57 | return &genericInformer{resource: resource.GroupResource(), informer: f.Scaling().V1alpha1().ScheduledScalers().Informer()}, nil 58 | 59 | } 60 | 61 | return nil, fmt.Errorf("no informer found for %v", resource) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/scaling/v1alpha1/mock_v1alpha1/scaling_client.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: k8s.restdev.com/operators/pkg/client/clientset/versioned/typed/scaling/v1alpha1 (interfaces: ScalingV1alpha1Interface) 3 | 4 | // Package mock_v1alpha1 is a generated GoMock package. 5 | package mock_v1alpha1 6 | 7 | import ( 8 | gomock "github.com/golang/mock/gomock" 9 | rest "k8s.io/client-go/rest" 10 | v1alpha1 "k8s.restdev.com/operators/pkg/client/clientset/versioned/typed/scaling/v1alpha1" 11 | reflect "reflect" 12 | ) 13 | 14 | // MockScalingV1alpha1Interface is a mock of ScalingV1alpha1Interface interface 15 | type MockScalingV1alpha1Interface struct { 16 | ctrl *gomock.Controller 17 | recorder *MockScalingV1alpha1InterfaceMockRecorder 18 | } 19 | 20 | // MockScalingV1alpha1InterfaceMockRecorder is the mock recorder for MockScalingV1alpha1Interface 21 | type MockScalingV1alpha1InterfaceMockRecorder struct { 22 | mock *MockScalingV1alpha1Interface 23 | } 24 | 25 | // NewMockScalingV1alpha1Interface creates a new mock instance 26 | func NewMockScalingV1alpha1Interface(ctrl *gomock.Controller) *MockScalingV1alpha1Interface { 27 | mock := &MockScalingV1alpha1Interface{ctrl: ctrl} 28 | mock.recorder = &MockScalingV1alpha1InterfaceMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use 33 | func (m *MockScalingV1alpha1Interface) EXPECT() *MockScalingV1alpha1InterfaceMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // RESTClient mocks base method 38 | func (m *MockScalingV1alpha1Interface) RESTClient() rest.Interface { 39 | m.ctrl.T.Helper() 40 | ret := m.ctrl.Call(m, "RESTClient") 41 | ret0, _ := ret[0].(rest.Interface) 42 | return ret0 43 | } 44 | 45 | // RESTClient indicates an expected call of RESTClient 46 | func (mr *MockScalingV1alpha1InterfaceMockRecorder) RESTClient() *gomock.Call { 47 | mr.mock.ctrl.T.Helper() 48 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RESTClient", reflect.TypeOf((*MockScalingV1alpha1Interface)(nil).RESTClient)) 49 | } 50 | 51 | // ScheduledScalers mocks base method 52 | func (m *MockScalingV1alpha1Interface) ScheduledScalers(arg0 string) v1alpha1.ScheduledScalerInterface { 53 | m.ctrl.T.Helper() 54 | ret := m.ctrl.Call(m, "ScheduledScalers", arg0) 55 | ret0, _ := ret[0].(v1alpha1.ScheduledScalerInterface) 56 | return ret0 57 | } 58 | 59 | // ScheduledScalers indicates an expected call of ScheduledScalers 60 | func (mr *MockScalingV1alpha1InterfaceMockRecorder) ScheduledScalers(arg0 interface{}) *gomock.Call { 61 | mr.mock.ctrl.T.Helper() 62 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScheduledScalers", reflect.TypeOf((*MockScalingV1alpha1Interface)(nil).ScheduledScalers), arg0) 63 | } 64 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/mock_versioned/clientset.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: clientset.go 3 | 4 | // Package mock_versioned is a generated GoMock package. 5 | package mock_versioned 6 | 7 | import ( 8 | gomock "github.com/golang/mock/gomock" 9 | discovery "k8s.io/client-go/discovery" 10 | v1alpha1 "k8s.restdev.com/operators/pkg/client/clientset/versioned/typed/scaling/v1alpha1" 11 | reflect "reflect" 12 | ) 13 | 14 | // MockInterface is a mock of Interface interface 15 | type MockInterface struct { 16 | ctrl *gomock.Controller 17 | recorder *MockInterfaceMockRecorder 18 | } 19 | 20 | // MockInterfaceMockRecorder is the mock recorder for MockInterface 21 | type MockInterfaceMockRecorder struct { 22 | mock *MockInterface 23 | } 24 | 25 | // NewMockInterface creates a new mock instance 26 | func NewMockInterface(ctrl *gomock.Controller) *MockInterface { 27 | mock := &MockInterface{ctrl: ctrl} 28 | mock.recorder = &MockInterfaceMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use 33 | func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // Discovery mocks base method 38 | func (m *MockInterface) Discovery() discovery.DiscoveryInterface { 39 | m.ctrl.T.Helper() 40 | ret := m.ctrl.Call(m, "Discovery") 41 | ret0, _ := ret[0].(discovery.DiscoveryInterface) 42 | return ret0 43 | } 44 | 45 | // Discovery indicates an expected call of Discovery 46 | func (mr *MockInterfaceMockRecorder) Discovery() *gomock.Call { 47 | mr.mock.ctrl.T.Helper() 48 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Discovery", reflect.TypeOf((*MockInterface)(nil).Discovery)) 49 | } 50 | 51 | // ScalingV1alpha1 mocks base method 52 | func (m *MockInterface) ScalingV1alpha1() v1alpha1.ScalingV1alpha1Interface { 53 | m.ctrl.T.Helper() 54 | ret := m.ctrl.Call(m, "ScalingV1alpha1") 55 | ret0, _ := ret[0].(v1alpha1.ScalingV1alpha1Interface) 56 | return ret0 57 | } 58 | 59 | // ScalingV1alpha1 indicates an expected call of ScalingV1alpha1 60 | func (mr *MockInterfaceMockRecorder) ScalingV1alpha1() *gomock.Call { 61 | mr.mock.ctrl.T.Helper() 62 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScalingV1alpha1", reflect.TypeOf((*MockInterface)(nil).ScalingV1alpha1)) 63 | } 64 | 65 | // Scaling mocks base method 66 | func (m *MockInterface) Scaling() v1alpha1.ScalingV1alpha1Interface { 67 | m.ctrl.T.Helper() 68 | ret := m.ctrl.Call(m, "Scaling") 69 | ret0, _ := ret[0].(v1alpha1.ScalingV1alpha1Interface) 70 | return ret0 71 | } 72 | 73 | // Scaling indicates an expected call of Scaling 74 | func (mr *MockInterfaceMockRecorder) Scaling() *gomock.Call { 75 | mr.mock.ctrl.T.Helper() 76 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Scaling", reflect.TypeOf((*MockInterface)(nil).Scaling)) 77 | } 78 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/scaling/v1alpha1/scaling_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | //go:generate mockgen -destination=mock_$GOPACKAGE/$GOFILE k8s.restdev.com/operators/pkg/client/clientset/versioned/typed/scaling/v1alpha1 ScalingV1alpha1Interface 20 | 21 | import ( 22 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 23 | rest "k8s.io/client-go/rest" 24 | v1alpha1 "k8s.restdev.com/operators/pkg/apis/scaling/v1alpha1" 25 | "k8s.restdev.com/operators/pkg/client/clientset/versioned/scheme" 26 | ) 27 | 28 | type ScalingV1alpha1Interface interface { 29 | RESTClient() rest.Interface 30 | ScheduledScalersGetter 31 | } 32 | 33 | // ScalingV1alpha1Client is used to interact with features provided by the scaling.k8s.restdev.com group. 34 | type ScalingV1alpha1Client struct { 35 | restClient rest.Interface 36 | } 37 | 38 | func (c *ScalingV1alpha1Client) ScheduledScalers(namespace string) ScheduledScalerInterface { 39 | return newScheduledScalers(c, namespace) 40 | } 41 | 42 | // NewForConfig creates a new ScalingV1alpha1Client for the given config. 43 | func NewForConfig(c *rest.Config) (*ScalingV1alpha1Client, error) { 44 | config := *c 45 | if err := setConfigDefaults(&config); err != nil { 46 | return nil, err 47 | } 48 | client, err := rest.RESTClientFor(&config) 49 | if err != nil { 50 | return nil, err 51 | } 52 | return &ScalingV1alpha1Client{client}, nil 53 | } 54 | 55 | // NewForConfigOrDie creates a new ScalingV1alpha1Client for the given config and 56 | // panics if there is an error in the config. 57 | func NewForConfigOrDie(c *rest.Config) *ScalingV1alpha1Client { 58 | client, err := NewForConfig(c) 59 | if err != nil { 60 | panic(err) 61 | } 62 | return client 63 | } 64 | 65 | // New creates a new ScalingV1alpha1Client for the given RESTClient. 66 | func New(c rest.Interface) *ScalingV1alpha1Client { 67 | return &ScalingV1alpha1Client{c} 68 | } 69 | 70 | func setConfigDefaults(config *rest.Config) error { 71 | gv := v1alpha1.SchemeGroupVersion 72 | config.GroupVersion = &gv 73 | config.APIPath = "/apis" 74 | config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} 75 | 76 | if config.UserAgent == "" { 77 | config.UserAgent = rest.DefaultKubernetesUserAgent() 78 | } 79 | 80 | return nil 81 | } 82 | 83 | // RESTClient returns a RESTClient that is used to communicate 84 | // with API server by this client implementation. 85 | func (c *ScalingV1alpha1Client) RESTClient() rest.Interface { 86 | if c == nil { 87 | return nil 88 | } 89 | return c.restClient 90 | } 91 | -------------------------------------------------------------------------------- /artifacts/kubes/scaling/chart/README.md: -------------------------------------------------------------------------------- 1 | # Helm Chart for scheduled Scaler 2 | 3 | ## Introduction 4 | 5 | This chart bootstraps a scheduled-scaler controller and allows the deployment of `ScheduledScaler` resources to your cluster. 6 | 7 | For more information read [scheduled-scaler overview](http://k8s.restdev.com/p/scheduled-scaler.html) 8 | 9 | ## Prerequisites 10 | 11 | * Kubernetes Version: 1.7+ 12 | * Kubernetes Cluster Settings: 13 | * "Legacy authorization": "Enabled" 14 | 15 | ## Installing the Chart 16 | 17 | ```bash 18 | $ helm install scheduled-scaler . 19 | ``` 20 | 21 | ## Uninstalling the Chart 22 | 23 | ```bash 24 | $ helm delete --purge scheduled-scaler 25 | ``` 26 | 27 | ## Configuration 28 | 29 | | Parameter | Description | Default | 30 | |-----------------------------------------|---------------------------------------------------------------------|--------------------------------------| 31 | | `image.repository` | Scheduled scaler container image | `k8srestdev/scaling` | 32 | | `image.tag` | Scheduled scaler container image tag | `0.0.4` | 33 | | `image.pullPolicy` | Scheduled scaler container image pull policy | `Always` | 34 | | `replicaCount` | Number of scheduled-scaler replicas to create (only 1 is supported) | `1` | 35 | | `sslCerts.hostPath` | TLS certs for secure connections | `/etc/ssl/certs` | 36 | | `rbac.create` | install required RBAC service account, roles and rolebindings | `true` | 37 | | `resources` | Resource configuration for Scheduled scaler controller pod | `{}` | 38 | | `nodeSelector` | Node labels for Scheduled scaler controller pod assignment | `{}` | 39 | | `tolerations` | Tolerations for Scheduled scaler controller pod assignment | `[]` | 40 | | `affinity` | Affinity Rules for Scheduled scaler controller pod assignment | `[]` | 41 | 42 | ## RBAC 43 | 44 | By default the chart will install the recommended RBAC roles and rolebindings. 45 | 46 | To determine if your cluster supports this running the following: 47 | 48 | ```bash 49 | $ kubectl api-versions | grep rbac 50 | ``` 51 | 52 | You also need to have the following parameter on the api server. See the following document for how to enable RBAC 53 | 54 | ```bash 55 | --authorization-mode=RBAC 56 | ``` 57 | 58 | If the output contains "beta" or both "alpha" and "beta" you can may install rbac by default, if not, you may turn RBAC off as described below. 59 | 60 | ### RBAC role/rolebinding creation 61 | 62 | RBAC resources are enabled by default. To disable RBAC do the following: 63 | 64 | ```bash 65 | $ helm install . --name scheduled-scaler --set rbac.create=false 66 | ``` 67 | -------------------------------------------------------------------------------- /pkg/services/scaling/cron/mock_cron/proxy.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: proxy.go 3 | 4 | // Package mock_cron is a generated GoMock package. 5 | package mock_cron 6 | 7 | import ( 8 | gomock "github.com/golang/mock/gomock" 9 | cron "github.com/robfig/cron" 10 | reflect "reflect" 11 | ) 12 | 13 | // MockCronProxy is a mock of CronProxy interface 14 | type MockCronProxy struct { 15 | ctrl *gomock.Controller 16 | recorder *MockCronProxyMockRecorder 17 | } 18 | 19 | // MockCronProxyMockRecorder is the mock recorder for MockCronProxy 20 | type MockCronProxyMockRecorder struct { 21 | mock *MockCronProxy 22 | } 23 | 24 | // NewMockCronProxy creates a new mock instance 25 | func NewMockCronProxy(ctrl *gomock.Controller) *MockCronProxy { 26 | mock := &MockCronProxy{ctrl: ctrl} 27 | mock.recorder = &MockCronProxyMockRecorder{mock} 28 | return mock 29 | } 30 | 31 | // EXPECT returns an object that allows the caller to indicate expected use 32 | func (m *MockCronProxy) EXPECT() *MockCronProxyMockRecorder { 33 | return m.recorder 34 | } 35 | 36 | // Parse mocks base method 37 | func (m *MockCronProxy) Parse(spec string) (cron.Schedule, error) { 38 | m.ctrl.T.Helper() 39 | ret := m.ctrl.Call(m, "Parse", spec) 40 | ret0, _ := ret[0].(cron.Schedule) 41 | ret1, _ := ret[1].(error) 42 | return ret0, ret1 43 | } 44 | 45 | // Parse indicates an expected call of Parse 46 | func (mr *MockCronProxyMockRecorder) Parse(spec interface{}) *gomock.Call { 47 | mr.mock.ctrl.T.Helper() 48 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Parse", reflect.TypeOf((*MockCronProxy)(nil).Parse), spec) 49 | } 50 | 51 | // Create mocks base method 52 | func (m *MockCronProxy) Create(timeZone string) (*cron.Cron, error) { 53 | m.ctrl.T.Helper() 54 | ret := m.ctrl.Call(m, "Create", timeZone) 55 | ret0, _ := ret[0].(*cron.Cron) 56 | ret1, _ := ret[1].(error) 57 | return ret0, ret1 58 | } 59 | 60 | // Create indicates an expected call of Create 61 | func (mr *MockCronProxyMockRecorder) Create(timeZone interface{}) *gomock.Call { 62 | mr.mock.ctrl.T.Helper() 63 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockCronProxy)(nil).Create), timeZone) 64 | } 65 | 66 | // Push mocks base method 67 | func (m *MockCronProxy) Push(c *cron.Cron, time string, call func()) { 68 | m.ctrl.T.Helper() 69 | m.ctrl.Call(m, "Push", c, time, call) 70 | } 71 | 72 | // Push indicates an expected call of Push 73 | func (mr *MockCronProxyMockRecorder) Push(c, time, call interface{}) *gomock.Call { 74 | mr.mock.ctrl.T.Helper() 75 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Push", reflect.TypeOf((*MockCronProxy)(nil).Push), c, time, call) 76 | } 77 | 78 | // Start mocks base method 79 | func (m *MockCronProxy) Start(c *cron.Cron) { 80 | m.ctrl.T.Helper() 81 | m.ctrl.Call(m, "Start", c) 82 | } 83 | 84 | // Start indicates an expected call of Start 85 | func (mr *MockCronProxyMockRecorder) Start(c interface{}) *gomock.Call { 86 | mr.mock.ctrl.T.Helper() 87 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockCronProxy)(nil).Start), c) 88 | } 89 | 90 | // Stop mocks base method 91 | func (m *MockCronProxy) Stop(c *cron.Cron) { 92 | m.ctrl.T.Helper() 93 | m.ctrl.Call(m, "Stop", c) 94 | } 95 | 96 | // Stop indicates an expected call of Stop 97 | func (mr *MockCronProxyMockRecorder) Stop(c interface{}) *gomock.Call { 98 | mr.mock.ctrl.T.Helper() 99 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockCronProxy)(nil).Stop), c) 100 | } 101 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/clientset.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package versioned 18 | 19 | //go:generate mockgen -source=$GOFILE -destination=mock_$GOPACKAGE/$GOFILE -package mock_$GOPACKAGE 20 | 21 | import ( 22 | glog "github.com/golang/glog" 23 | "k8s.io/client-go/discovery" 24 | rest "k8s.io/client-go/rest" 25 | flowcontrol "k8s.io/client-go/util/flowcontrol" 26 | scalingv1alpha1 "k8s.restdev.com/operators/pkg/client/clientset/versioned/typed/scaling/v1alpha1" 27 | ) 28 | 29 | type Interface interface { 30 | Discovery() discovery.DiscoveryInterface 31 | ScalingV1alpha1() scalingv1alpha1.ScalingV1alpha1Interface 32 | // Deprecated: please explicitly pick a version if possible. 33 | Scaling() scalingv1alpha1.ScalingV1alpha1Interface 34 | } 35 | 36 | // Clientset contains the clients for groups. Each group has exactly one 37 | // version included in a Clientset. 38 | type Clientset struct { 39 | *discovery.DiscoveryClient 40 | scalingV1alpha1 *scalingv1alpha1.ScalingV1alpha1Client 41 | } 42 | 43 | // ScalingV1alpha1 retrieves the ScalingV1alpha1Client 44 | func (c *Clientset) ScalingV1alpha1() scalingv1alpha1.ScalingV1alpha1Interface { 45 | return c.scalingV1alpha1 46 | } 47 | 48 | // Deprecated: Scaling retrieves the default version of ScalingClient. 49 | // Please explicitly pick a version. 50 | func (c *Clientset) Scaling() scalingv1alpha1.ScalingV1alpha1Interface { 51 | return c.scalingV1alpha1 52 | } 53 | 54 | // Discovery retrieves the DiscoveryClient 55 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 56 | if c == nil { 57 | return nil 58 | } 59 | return c.DiscoveryClient 60 | } 61 | 62 | // NewForConfig creates a new Clientset for the given config. 63 | func NewForConfig(c *rest.Config) (*Clientset, error) { 64 | configShallowCopy := *c 65 | if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { 66 | configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) 67 | } 68 | var cs Clientset 69 | var err error 70 | cs.scalingV1alpha1, err = scalingv1alpha1.NewForConfig(&configShallowCopy) 71 | if err != nil { 72 | return nil, err 73 | } 74 | 75 | cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) 76 | if err != nil { 77 | glog.Errorf("failed to create the DiscoveryClient: %v", err) 78 | return nil, err 79 | } 80 | return &cs, nil 81 | } 82 | 83 | // NewForConfigOrDie creates a new Clientset for the given config and 84 | // panics if there is an error in the config. 85 | func NewForConfigOrDie(c *rest.Config) *Clientset { 86 | var cs Clientset 87 | cs.scalingV1alpha1 = scalingv1alpha1.NewForConfigOrDie(c) 88 | 89 | cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) 90 | return &cs 91 | } 92 | 93 | // New creates a new Clientset for the given RESTClient. 94 | func New(c rest.Interface) *Clientset { 95 | var cs Clientset 96 | cs.scalingV1alpha1 = scalingv1alpha1.New(c) 97 | 98 | cs.DiscoveryClient = discovery.NewDiscoveryClient(c) 99 | return &cs 100 | } 101 | -------------------------------------------------------------------------------- /artifacts/kubes/scaling/crd.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | # name must match the spec fields below, and be in the form: . 5 | name: scheduledscalers.scaling.k8s.restdev.com 6 | spec: 7 | # group name to use for REST API: /apis// 8 | group: scaling.k8s.restdev.com 9 | versions: 10 | # version name to use for REST API: /apis// 11 | - name: v1alpha1 12 | served: true 13 | storage: true 14 | schema: 15 | openAPIV3Schema: 16 | description: "ScheduledScaler is the Schema for the scheduled-scaler API" 17 | type: object 18 | properties: 19 | spec: 20 | description: "ScheduledScalerSpec is the specification of a ScheduledScaler" 21 | type: object 22 | properties: 23 | timeZone: 24 | description: "Timezone to use for the cronjob" 25 | type: string 26 | target: 27 | description: "Target to scale" 28 | type: object 29 | properties: 30 | kind: 31 | description: "Kind of the target (InstanceGroup/HorizontalPodAutoscaler)" 32 | type: string 33 | name: 34 | description: "Name of the target resource" 35 | type: string 36 | apiVersion: 37 | description: "API version of the target resource" 38 | type: string 39 | required: ["kind", "name", "apiVersion"] 40 | steps: 41 | description: "List of steps to scale the target resource at a specific time." 42 | type: array 43 | items: 44 | description: "Step to scale the target resource at a specific time." 45 | type: object 46 | properties: 47 | runat: 48 | description: "Cronjob time string (gocron) to run the scaling. Uses Cron Expression Format, https://pkg.go.dev/github.com/robfig/cron#hdr-CRON_Expression_Format" 49 | type: string 50 | pattern: "^(((\\d+|\\d+(-|\\/)\\d+)(,(\\d+|\\d+(-|\\/)\\d+))*|\\*) ){5}((\\d+|\\d+(-|\\/)\\d+)(,(\\d+|\\d+(-|\\/)\\d+))*|\\*)$" 51 | mode: 52 | description: "Type of scaling to run. 'fixed': set replicas to a fixed value, 'range': set replicas to a range" 53 | type: string 54 | pattern: "^(fixed|range)$" 55 | replicas: 56 | description: "Number of replicas to set when mode is 'fixed'" 57 | type: integer 58 | minReplicas: 59 | description: "Minimum number of replicas to set when mode is 'range'" 60 | type: integer 61 | maxReplicas: 62 | description: "Maximum number of replicas to set when mode is 'range'" 63 | type: integer 64 | required: ["runat", "mode"] 65 | required: ["target", "steps"] 66 | # either Namespaced or Cluster 67 | scope: Namespaced 68 | names: 69 | # plural name to be used in the URL: /apis/// 70 | plural: scheduledscalers 71 | # singular name to be used as an alias on the CLI and for display 72 | singular: scheduledscaler 73 | # kind is normally the CamelCased singular type. Your resource manifests use this. 74 | kind: ScheduledScaler 75 | # shortNames allow shorter string to match your resource on the CLI 76 | shortNames: 77 | - ss 78 | -------------------------------------------------------------------------------- /artifacts/kubes/scaling/chart/templates/crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | # name must match the spec fields below, and be in the form: . 5 | name: scheduledscalers.scaling.k8s.restdev.com 6 | spec: 7 | # group name to use for REST API: /apis// 8 | group: scaling.k8s.restdev.com 9 | versions: 10 | # version name to use for REST API: /apis// 11 | - name: v1alpha1 12 | served: true 13 | storage: true 14 | schema: 15 | openAPIV3Schema: 16 | description: "ScheduledScaler is the Schema for the scheduled-scaler API" 17 | type: object 18 | properties: 19 | spec: 20 | description: "ScheduledScalerSpec is the specification of a ScheduledScaler" 21 | type: object 22 | properties: 23 | timeZone: 24 | description: "Timezone to use for the cronjob" 25 | type: string 26 | target: 27 | description: "Target to scale" 28 | type: object 29 | properties: 30 | kind: 31 | description: "Kind of the target (InstanceGroup/HorizontalPodAutoscaler)" 32 | type: string 33 | name: 34 | description: "Name of the target resource" 35 | type: string 36 | apiVersion: 37 | description: "API version of the target resource" 38 | type: string 39 | required: ["kind", "name", "apiVersion"] 40 | steps: 41 | description: "List of steps to scale the target resource at a specific time." 42 | type: array 43 | items: 44 | description: "Step to scale the target resource at a specific time." 45 | type: object 46 | properties: 47 | runat: 48 | description: "Cronjob time string (gocron) to run the scaling. Uses Cron Expression Format, https://pkg.go.dev/github.com/robfig/cron#hdr-CRON_Expression_Format" 49 | type: string 50 | pattern: "^(((\\d+|\\d+(-|\\/)\\d+)(,(\\d+|\\d+(-|\\/)\\d+))*|\\*) ){5}((\\d+|\\d+(-|\\/)\\d+)(,(\\d+|\\d+(-|\\/)\\d+))*|\\*)$" 51 | mode: 52 | description: "Type of scaling to run. 'fixed': set replicas to a fixed value, 'range': set replicas to a range" 53 | type: string 54 | pattern: "^(fixed|range)$" 55 | replicas: 56 | description: "Number of replicas to set when mode is 'fixed'" 57 | type: integer 58 | minReplicas: 59 | description: "Minimum number of replicas to set when mode is 'range'" 60 | type: integer 61 | maxReplicas: 62 | description: "Maximum number of replicas to set when mode is 'range'" 63 | type: integer 64 | required: ["runat", "mode"] 65 | required: ["target", "steps"] 66 | # either Namespaced or Cluster 67 | scope: Namespaced 68 | names: 69 | # plural name to be used in the URL: /apis/// 70 | plural: scheduledscalers 71 | # singular name to be used as an alias on the CLI and for display 72 | singular: scheduledscaler 73 | # kind is normally the CamelCased singular type. Your resource manifests use this. 74 | kind: ScheduledScaler 75 | # shortNames allow shorter string to match your resource on the CLI 76 | shortNames: 77 | - ss 78 | -------------------------------------------------------------------------------- /pkg/client/listers/scaling/v1alpha1/scheduledscaler.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // This file was automatically generated by lister-gen 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | "k8s.io/apimachinery/pkg/api/errors" 23 | "k8s.io/apimachinery/pkg/labels" 24 | "k8s.io/client-go/tools/cache" 25 | v1alpha1 "k8s.restdev.com/operators/pkg/apis/scaling/v1alpha1" 26 | ) 27 | 28 | // ScheduledScalerLister helps list ScheduledScalers. 29 | type ScheduledScalerLister interface { 30 | // List lists all ScheduledScalers in the indexer. 31 | List(selector labels.Selector) (ret []*v1alpha1.ScheduledScaler, err error) 32 | // ScheduledScalers returns an object that can list and get ScheduledScalers. 33 | ScheduledScalers(namespace string) ScheduledScalerNamespaceLister 34 | ScheduledScalerListerExpansion 35 | } 36 | 37 | // scheduledScalerLister implements the ScheduledScalerLister interface. 38 | type scheduledScalerLister struct { 39 | indexer cache.Indexer 40 | } 41 | 42 | // NewScheduledScalerLister returns a new ScheduledScalerLister. 43 | func NewScheduledScalerLister(indexer cache.Indexer) ScheduledScalerLister { 44 | return &scheduledScalerLister{indexer: indexer} 45 | } 46 | 47 | // List lists all ScheduledScalers in the indexer. 48 | func (s *scheduledScalerLister) List(selector labels.Selector) (ret []*v1alpha1.ScheduledScaler, err error) { 49 | err = cache.ListAll(s.indexer, selector, func(m interface{}) { 50 | ret = append(ret, m.(*v1alpha1.ScheduledScaler)) 51 | }) 52 | return ret, err 53 | } 54 | 55 | // ScheduledScalers returns an object that can list and get ScheduledScalers. 56 | func (s *scheduledScalerLister) ScheduledScalers(namespace string) ScheduledScalerNamespaceLister { 57 | return scheduledScalerNamespaceLister{indexer: s.indexer, namespace: namespace} 58 | } 59 | 60 | // ScheduledScalerNamespaceLister helps list and get ScheduledScalers. 61 | type ScheduledScalerNamespaceLister interface { 62 | // List lists all ScheduledScalers in the indexer for a given namespace. 63 | List(selector labels.Selector) (ret []*v1alpha1.ScheduledScaler, err error) 64 | // Get retrieves the ScheduledScaler from the indexer for a given namespace and name. 65 | Get(name string) (*v1alpha1.ScheduledScaler, error) 66 | ScheduledScalerNamespaceListerExpansion 67 | } 68 | 69 | // scheduledScalerNamespaceLister implements the ScheduledScalerNamespaceLister 70 | // interface. 71 | type scheduledScalerNamespaceLister struct { 72 | indexer cache.Indexer 73 | namespace string 74 | } 75 | 76 | // List lists all ScheduledScalers in the indexer for a given namespace. 77 | func (s scheduledScalerNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.ScheduledScaler, err error) { 78 | err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { 79 | ret = append(ret, m.(*v1alpha1.ScheduledScaler)) 80 | }) 81 | return ret, err 82 | } 83 | 84 | // Get retrieves the ScheduledScaler from the indexer for a given namespace and name. 85 | func (s scheduledScalerNamespaceLister) Get(name string) (*v1alpha1.ScheduledScaler, error) { 86 | obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) 87 | if err != nil { 88 | return nil, err 89 | } 90 | if !exists { 91 | return nil, errors.NewNotFound(v1alpha1.Resource("scheduledscaler"), name) 92 | } 93 | return obj.(*v1alpha1.ScheduledScaler), nil 94 | } 95 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/scaling/v1alpha1/scheduledscaler.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // This file was automatically generated by informer-gen 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | time "time" 23 | 24 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | runtime "k8s.io/apimachinery/pkg/runtime" 26 | watch "k8s.io/apimachinery/pkg/watch" 27 | cache "k8s.io/client-go/tools/cache" 28 | scaling_v1alpha1 "k8s.restdev.com/operators/pkg/apis/scaling/v1alpha1" 29 | versioned "k8s.restdev.com/operators/pkg/client/clientset/versioned" 30 | internalinterfaces "k8s.restdev.com/operators/pkg/client/informers/externalversions/internalinterfaces" 31 | v1alpha1 "k8s.restdev.com/operators/pkg/client/listers/scaling/v1alpha1" 32 | ) 33 | 34 | // ScheduledScalerInformer provides access to a shared informer and lister for 35 | // ScheduledScalers. 36 | type ScheduledScalerInformer interface { 37 | Informer() cache.SharedIndexInformer 38 | Lister() v1alpha1.ScheduledScalerLister 39 | } 40 | 41 | type scheduledScalerInformer struct { 42 | factory internalinterfaces.SharedInformerFactory 43 | tweakListOptions internalinterfaces.TweakListOptionsFunc 44 | namespace string 45 | } 46 | 47 | // NewScheduledScalerInformer constructs a new informer for ScheduledScaler type. 48 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 49 | // one. This reduces memory footprint and number of connections to the server. 50 | func NewScheduledScalerInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { 51 | return NewFilteredScheduledScalerInformer(client, namespace, resyncPeriod, indexers, nil) 52 | } 53 | 54 | // NewFilteredScheduledScalerInformer constructs a new informer for ScheduledScaler type. 55 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 56 | // one. This reduces memory footprint and number of connections to the server. 57 | func NewFilteredScheduledScalerInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { 58 | return cache.NewSharedIndexInformer( 59 | &cache.ListWatch{ 60 | ListFunc: func(options v1.ListOptions) (runtime.Object, error) { 61 | if tweakListOptions != nil { 62 | tweakListOptions(&options) 63 | } 64 | return client.ScalingV1alpha1().ScheduledScalers(namespace).List(options) 65 | }, 66 | WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { 67 | if tweakListOptions != nil { 68 | tweakListOptions(&options) 69 | } 70 | return client.ScalingV1alpha1().ScheduledScalers(namespace).Watch(options) 71 | }, 72 | }, 73 | &scaling_v1alpha1.ScheduledScaler{}, 74 | resyncPeriod, 75 | indexers, 76 | ) 77 | } 78 | 79 | func (f *scheduledScalerInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { 80 | return NewFilteredScheduledScalerInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) 81 | } 82 | 83 | func (f *scheduledScalerInformer) Informer() cache.SharedIndexInformer { 84 | return f.factory.InformerFor(&scaling_v1alpha1.ScheduledScaler{}, f.defaultInformer) 85 | } 86 | 87 | func (f *scheduledScalerInformer) Lister() v1alpha1.ScheduledScalerLister { 88 | return v1alpha1.NewScheduledScalerLister(f.Informer().GetIndexer()) 89 | } 90 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // This file was automatically generated by informer-gen 18 | 19 | package externalversions 20 | 21 | import ( 22 | reflect "reflect" 23 | sync "sync" 24 | time "time" 25 | 26 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 | runtime "k8s.io/apimachinery/pkg/runtime" 28 | schema "k8s.io/apimachinery/pkg/runtime/schema" 29 | cache "k8s.io/client-go/tools/cache" 30 | versioned "k8s.restdev.com/operators/pkg/client/clientset/versioned" 31 | internalinterfaces "k8s.restdev.com/operators/pkg/client/informers/externalversions/internalinterfaces" 32 | scaling "k8s.restdev.com/operators/pkg/client/informers/externalversions/scaling" 33 | ) 34 | 35 | type sharedInformerFactory struct { 36 | client versioned.Interface 37 | namespace string 38 | tweakListOptions internalinterfaces.TweakListOptionsFunc 39 | lock sync.Mutex 40 | defaultResync time.Duration 41 | 42 | informers map[reflect.Type]cache.SharedIndexInformer 43 | // startedInformers is used for tracking which informers have been started. 44 | // This allows Start() to be called multiple times safely. 45 | startedInformers map[reflect.Type]bool 46 | } 47 | 48 | // NewSharedInformerFactory constructs a new instance of sharedInformerFactory 49 | func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { 50 | return NewFilteredSharedInformerFactory(client, defaultResync, v1.NamespaceAll, nil) 51 | } 52 | 53 | // NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. 54 | // Listers obtained via this SharedInformerFactory will be subject to the same filters 55 | // as specified here. 56 | func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { 57 | return &sharedInformerFactory{ 58 | client: client, 59 | namespace: namespace, 60 | tweakListOptions: tweakListOptions, 61 | defaultResync: defaultResync, 62 | informers: make(map[reflect.Type]cache.SharedIndexInformer), 63 | startedInformers: make(map[reflect.Type]bool), 64 | } 65 | } 66 | 67 | // Start initializes all requested informers. 68 | func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { 69 | f.lock.Lock() 70 | defer f.lock.Unlock() 71 | 72 | for informerType, informer := range f.informers { 73 | if !f.startedInformers[informerType] { 74 | go informer.Run(stopCh) 75 | f.startedInformers[informerType] = true 76 | } 77 | } 78 | } 79 | 80 | // WaitForCacheSync waits for all started informers' cache were synced. 81 | func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { 82 | informers := func() map[reflect.Type]cache.SharedIndexInformer { 83 | f.lock.Lock() 84 | defer f.lock.Unlock() 85 | 86 | informers := map[reflect.Type]cache.SharedIndexInformer{} 87 | for informerType, informer := range f.informers { 88 | if f.startedInformers[informerType] { 89 | informers[informerType] = informer 90 | } 91 | } 92 | return informers 93 | }() 94 | 95 | res := map[reflect.Type]bool{} 96 | for informType, informer := range informers { 97 | res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) 98 | } 99 | return res 100 | } 101 | 102 | // InternalInformerFor returns the SharedIndexInformer for obj using an internal 103 | // client. 104 | func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { 105 | f.lock.Lock() 106 | defer f.lock.Unlock() 107 | 108 | informerType := reflect.TypeOf(obj) 109 | informer, exists := f.informers[informerType] 110 | if exists { 111 | return informer 112 | } 113 | informer = newFunc(f.client, f.defaultResync) 114 | f.informers[informerType] = informer 115 | 116 | return informer 117 | } 118 | 119 | // SharedInformerFactory provides shared informers for resources in all known 120 | // API group versions. 121 | type SharedInformerFactory interface { 122 | internalinterfaces.SharedInformerFactory 123 | ForResource(resource schema.GroupVersionResource) (GenericInformer, error) 124 | WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool 125 | 126 | Scaling() scaling.Interface 127 | } 128 | 129 | func (f *sharedInformerFactory) Scaling() scaling.Interface { 130 | return scaling.New(f, f.namespace, f.tweakListOptions) 131 | } 132 | -------------------------------------------------------------------------------- /pkg/client/listers/scaling/v1alpha1/mock_v1alpha1/scheduledscaler.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: k8s.restdev.com/operators/pkg/client/listers/scaling/v1alpha1 (interfaces: ScheduledScalerLister,ScheduledScalerNamespaceLister) 3 | 4 | // Package mock_v1alpha1 is a generated GoMock package. 5 | package mock_v1alpha1 6 | 7 | import ( 8 | gomock "github.com/golang/mock/gomock" 9 | labels "k8s.io/apimachinery/pkg/labels" 10 | v1alpha1 "k8s.restdev.com/operators/pkg/apis/scaling/v1alpha1" 11 | v1alpha10 "k8s.restdev.com/operators/pkg/client/listers/scaling/v1alpha1" 12 | reflect "reflect" 13 | ) 14 | 15 | // MockScheduledScalerLister is a mock of ScheduledScalerLister interface 16 | type MockScheduledScalerLister struct { 17 | ctrl *gomock.Controller 18 | recorder *MockScheduledScalerListerMockRecorder 19 | } 20 | 21 | // MockScheduledScalerListerMockRecorder is the mock recorder for MockScheduledScalerLister 22 | type MockScheduledScalerListerMockRecorder struct { 23 | mock *MockScheduledScalerLister 24 | } 25 | 26 | // NewMockScheduledScalerLister creates a new mock instance 27 | func NewMockScheduledScalerLister(ctrl *gomock.Controller) *MockScheduledScalerLister { 28 | mock := &MockScheduledScalerLister{ctrl: ctrl} 29 | mock.recorder = &MockScheduledScalerListerMockRecorder{mock} 30 | return mock 31 | } 32 | 33 | // EXPECT returns an object that allows the caller to indicate expected use 34 | func (m *MockScheduledScalerLister) EXPECT() *MockScheduledScalerListerMockRecorder { 35 | return m.recorder 36 | } 37 | 38 | // List mocks base method 39 | func (m *MockScheduledScalerLister) List(arg0 labels.Selector) ([]*v1alpha1.ScheduledScaler, error) { 40 | m.ctrl.T.Helper() 41 | ret := m.ctrl.Call(m, "List", arg0) 42 | ret0, _ := ret[0].([]*v1alpha1.ScheduledScaler) 43 | ret1, _ := ret[1].(error) 44 | return ret0, ret1 45 | } 46 | 47 | // List indicates an expected call of List 48 | func (mr *MockScheduledScalerListerMockRecorder) List(arg0 interface{}) *gomock.Call { 49 | mr.mock.ctrl.T.Helper() 50 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockScheduledScalerLister)(nil).List), arg0) 51 | } 52 | 53 | // ScheduledScalers mocks base method 54 | func (m *MockScheduledScalerLister) ScheduledScalers(arg0 string) v1alpha10.ScheduledScalerNamespaceLister { 55 | m.ctrl.T.Helper() 56 | ret := m.ctrl.Call(m, "ScheduledScalers", arg0) 57 | ret0, _ := ret[0].(v1alpha10.ScheduledScalerNamespaceLister) 58 | return ret0 59 | } 60 | 61 | // ScheduledScalers indicates an expected call of ScheduledScalers 62 | func (mr *MockScheduledScalerListerMockRecorder) ScheduledScalers(arg0 interface{}) *gomock.Call { 63 | mr.mock.ctrl.T.Helper() 64 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScheduledScalers", reflect.TypeOf((*MockScheduledScalerLister)(nil).ScheduledScalers), arg0) 65 | } 66 | 67 | // MockScheduledScalerNamespaceLister is a mock of ScheduledScalerNamespaceLister interface 68 | type MockScheduledScalerNamespaceLister struct { 69 | ctrl *gomock.Controller 70 | recorder *MockScheduledScalerNamespaceListerMockRecorder 71 | } 72 | 73 | // MockScheduledScalerNamespaceListerMockRecorder is the mock recorder for MockScheduledScalerNamespaceLister 74 | type MockScheduledScalerNamespaceListerMockRecorder struct { 75 | mock *MockScheduledScalerNamespaceLister 76 | } 77 | 78 | // NewMockScheduledScalerNamespaceLister creates a new mock instance 79 | func NewMockScheduledScalerNamespaceLister(ctrl *gomock.Controller) *MockScheduledScalerNamespaceLister { 80 | mock := &MockScheduledScalerNamespaceLister{ctrl: ctrl} 81 | mock.recorder = &MockScheduledScalerNamespaceListerMockRecorder{mock} 82 | return mock 83 | } 84 | 85 | // EXPECT returns an object that allows the caller to indicate expected use 86 | func (m *MockScheduledScalerNamespaceLister) EXPECT() *MockScheduledScalerNamespaceListerMockRecorder { 87 | return m.recorder 88 | } 89 | 90 | // Get mocks base method 91 | func (m *MockScheduledScalerNamespaceLister) Get(arg0 string) (*v1alpha1.ScheduledScaler, error) { 92 | m.ctrl.T.Helper() 93 | ret := m.ctrl.Call(m, "Get", arg0) 94 | ret0, _ := ret[0].(*v1alpha1.ScheduledScaler) 95 | ret1, _ := ret[1].(error) 96 | return ret0, ret1 97 | } 98 | 99 | // Get indicates an expected call of Get 100 | func (mr *MockScheduledScalerNamespaceListerMockRecorder) Get(arg0 interface{}) *gomock.Call { 101 | mr.mock.ctrl.T.Helper() 102 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockScheduledScalerNamespaceLister)(nil).Get), arg0) 103 | } 104 | 105 | // List mocks base method 106 | func (m *MockScheduledScalerNamespaceLister) List(arg0 labels.Selector) ([]*v1alpha1.ScheduledScaler, error) { 107 | m.ctrl.T.Helper() 108 | ret := m.ctrl.Call(m, "List", arg0) 109 | ret0, _ := ret[0].([]*v1alpha1.ScheduledScaler) 110 | ret1, _ := ret[1].(error) 111 | return ret0, ret1 112 | } 113 | 114 | // List indicates an expected call of List 115 | func (mr *MockScheduledScalerNamespaceListerMockRecorder) List(arg0 interface{}) *gomock.Call { 116 | mr.mock.ctrl.T.Helper() 117 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockScheduledScalerNamespaceLister)(nil).List), arg0) 118 | } 119 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/scaling/v1alpha1/fake/fake_scheduledscaler.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package fake 18 | 19 | import ( 20 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | labels "k8s.io/apimachinery/pkg/labels" 22 | schema "k8s.io/apimachinery/pkg/runtime/schema" 23 | types "k8s.io/apimachinery/pkg/types" 24 | watch "k8s.io/apimachinery/pkg/watch" 25 | testing "k8s.io/client-go/testing" 26 | v1alpha1 "k8s.restdev.com/operators/pkg/apis/scaling/v1alpha1" 27 | ) 28 | 29 | // FakeScheduledScalers implements ScheduledScalerInterface 30 | type FakeScheduledScalers struct { 31 | Fake *FakeScalingV1alpha1 32 | ns string 33 | } 34 | 35 | var scheduledscalersResource = schema.GroupVersionResource{Group: "scaling.k8s.restdev.com", Version: "v1alpha1", Resource: "scheduledscalers"} 36 | 37 | var scheduledscalersKind = schema.GroupVersionKind{Group: "scaling.k8s.restdev.com", Version: "v1alpha1", Kind: "ScheduledScaler"} 38 | 39 | // Get takes name of the scheduledScaler, and returns the corresponding scheduledScaler object, and an error if there is any. 40 | func (c *FakeScheduledScalers) Get(name string, options v1.GetOptions) (result *v1alpha1.ScheduledScaler, err error) { 41 | obj, err := c.Fake. 42 | Invokes(testing.NewGetAction(scheduledscalersResource, c.ns, name), &v1alpha1.ScheduledScaler{}) 43 | 44 | if obj == nil { 45 | return nil, err 46 | } 47 | return obj.(*v1alpha1.ScheduledScaler), err 48 | } 49 | 50 | // List takes label and field selectors, and returns the list of ScheduledScalers that match those selectors. 51 | func (c *FakeScheduledScalers) List(opts v1.ListOptions) (result *v1alpha1.ScheduledScalerList, err error) { 52 | obj, err := c.Fake. 53 | Invokes(testing.NewListAction(scheduledscalersResource, scheduledscalersKind, c.ns, opts), &v1alpha1.ScheduledScalerList{}) 54 | 55 | if obj == nil { 56 | return nil, err 57 | } 58 | 59 | label, _, _ := testing.ExtractFromListOptions(opts) 60 | if label == nil { 61 | label = labels.Everything() 62 | } 63 | list := &v1alpha1.ScheduledScalerList{} 64 | for _, item := range obj.(*v1alpha1.ScheduledScalerList).Items { 65 | if label.Matches(labels.Set(item.Labels)) { 66 | list.Items = append(list.Items, item) 67 | } 68 | } 69 | return list, err 70 | } 71 | 72 | // Watch returns a watch.Interface that watches the requested scheduledScalers. 73 | func (c *FakeScheduledScalers) Watch(opts v1.ListOptions) (watch.Interface, error) { 74 | return c.Fake. 75 | InvokesWatch(testing.NewWatchAction(scheduledscalersResource, c.ns, opts)) 76 | 77 | } 78 | 79 | // Create takes the representation of a scheduledScaler and creates it. Returns the server's representation of the scheduledScaler, and an error, if there is any. 80 | func (c *FakeScheduledScalers) Create(scheduledScaler *v1alpha1.ScheduledScaler) (result *v1alpha1.ScheduledScaler, err error) { 81 | obj, err := c.Fake. 82 | Invokes(testing.NewCreateAction(scheduledscalersResource, c.ns, scheduledScaler), &v1alpha1.ScheduledScaler{}) 83 | 84 | if obj == nil { 85 | return nil, err 86 | } 87 | return obj.(*v1alpha1.ScheduledScaler), err 88 | } 89 | 90 | // Update takes the representation of a scheduledScaler and updates it. Returns the server's representation of the scheduledScaler, and an error, if there is any. 91 | func (c *FakeScheduledScalers) Update(scheduledScaler *v1alpha1.ScheduledScaler) (result *v1alpha1.ScheduledScaler, err error) { 92 | obj, err := c.Fake. 93 | Invokes(testing.NewUpdateAction(scheduledscalersResource, c.ns, scheduledScaler), &v1alpha1.ScheduledScaler{}) 94 | 95 | if obj == nil { 96 | return nil, err 97 | } 98 | return obj.(*v1alpha1.ScheduledScaler), err 99 | } 100 | 101 | // Delete takes name of the scheduledScaler and deletes it. Returns an error if one occurs. 102 | func (c *FakeScheduledScalers) Delete(name string, options *v1.DeleteOptions) error { 103 | _, err := c.Fake. 104 | Invokes(testing.NewDeleteAction(scheduledscalersResource, c.ns, name), &v1alpha1.ScheduledScaler{}) 105 | 106 | return err 107 | } 108 | 109 | // DeleteCollection deletes a collection of objects. 110 | func (c *FakeScheduledScalers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { 111 | action := testing.NewDeleteCollectionAction(scheduledscalersResource, c.ns, listOptions) 112 | 113 | _, err := c.Fake.Invokes(action, &v1alpha1.ScheduledScalerList{}) 114 | return err 115 | } 116 | 117 | // Patch applies the patch and returns the patched scheduledScaler. 118 | func (c *FakeScheduledScalers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ScheduledScaler, err error) { 119 | obj, err := c.Fake. 120 | Invokes(testing.NewPatchSubresourceAction(scheduledscalersResource, c.ns, name, data, subresources...), &v1alpha1.ScheduledScaler{}) 121 | 122 | if obj == nil { 123 | return nil, err 124 | } 125 | return obj.(*v1alpha1.ScheduledScaler), err 126 | } 127 | -------------------------------------------------------------------------------- /pkg/apis/scaling/v1alpha1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | // +build !ignore_autogenerated 2 | 3 | /* 4 | Copyright 2018 The Kubernetes Authors. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | // This file was autogenerated by deepcopy-gen. Do not edit it manually! 20 | 21 | package v1alpha1 22 | 23 | import ( 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | ) 26 | 27 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 28 | func (in *ScheduledScaler) DeepCopyInto(out *ScheduledScaler) { 29 | *out = *in 30 | out.TypeMeta = in.TypeMeta 31 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 32 | in.Spec.DeepCopyInto(&out.Spec) 33 | out.Status = in.Status 34 | return 35 | } 36 | 37 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScheduledScaler. 38 | func (in *ScheduledScaler) DeepCopy() *ScheduledScaler { 39 | if in == nil { 40 | return nil 41 | } 42 | out := new(ScheduledScaler) 43 | in.DeepCopyInto(out) 44 | return out 45 | } 46 | 47 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 48 | func (in *ScheduledScaler) DeepCopyObject() runtime.Object { 49 | if c := in.DeepCopy(); c != nil { 50 | return c 51 | } else { 52 | return nil 53 | } 54 | } 55 | 56 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 57 | func (in *ScheduledScalerList) DeepCopyInto(out *ScheduledScalerList) { 58 | *out = *in 59 | out.TypeMeta = in.TypeMeta 60 | out.ListMeta = in.ListMeta 61 | if in.Items != nil { 62 | in, out := &in.Items, &out.Items 63 | *out = make([]ScheduledScaler, len(*in)) 64 | for i := range *in { 65 | (*in)[i].DeepCopyInto(&(*out)[i]) 66 | } 67 | } 68 | return 69 | } 70 | 71 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScheduledScalerList. 72 | func (in *ScheduledScalerList) DeepCopy() *ScheduledScalerList { 73 | if in == nil { 74 | return nil 75 | } 76 | out := new(ScheduledScalerList) 77 | in.DeepCopyInto(out) 78 | return out 79 | } 80 | 81 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 82 | func (in *ScheduledScalerList) DeepCopyObject() runtime.Object { 83 | if c := in.DeepCopy(); c != nil { 84 | return c 85 | } else { 86 | return nil 87 | } 88 | } 89 | 90 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 91 | func (in *ScheduledScalerSpec) DeepCopyInto(out *ScheduledScalerSpec) { 92 | *out = *in 93 | out.Target = in.Target 94 | if in.Steps != nil { 95 | in, out := &in.Steps, &out.Steps 96 | *out = make([]ScheduledScalerStep, len(*in)) 97 | for i := range *in { 98 | (*in)[i].DeepCopyInto(&(*out)[i]) 99 | } 100 | } 101 | return 102 | } 103 | 104 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScheduledScalerSpec. 105 | func (in *ScheduledScalerSpec) DeepCopy() *ScheduledScalerSpec { 106 | if in == nil { 107 | return nil 108 | } 109 | out := new(ScheduledScalerSpec) 110 | in.DeepCopyInto(out) 111 | return out 112 | } 113 | 114 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 115 | func (in *ScheduledScalerStatus) DeepCopyInto(out *ScheduledScalerStatus) { 116 | *out = *in 117 | return 118 | } 119 | 120 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScheduledScalerStatus. 121 | func (in *ScheduledScalerStatus) DeepCopy() *ScheduledScalerStatus { 122 | if in == nil { 123 | return nil 124 | } 125 | out := new(ScheduledScalerStatus) 126 | in.DeepCopyInto(out) 127 | return out 128 | } 129 | 130 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 131 | func (in *ScheduledScalerStep) DeepCopyInto(out *ScheduledScalerStep) { 132 | *out = *in 133 | if in.MinReplicas != nil { 134 | in, out := &in.MinReplicas, &out.MinReplicas 135 | if *in == nil { 136 | *out = nil 137 | } else { 138 | *out = new(int32) 139 | **out = **in 140 | } 141 | } 142 | if in.MaxReplicas != nil { 143 | in, out := &in.MaxReplicas, &out.MaxReplicas 144 | if *in == nil { 145 | *out = nil 146 | } else { 147 | *out = new(int32) 148 | **out = **in 149 | } 150 | } 151 | if in.Replicas != nil { 152 | in, out := &in.Replicas, &out.Replicas 153 | if *in == nil { 154 | *out = nil 155 | } else { 156 | *out = new(int32) 157 | **out = **in 158 | } 159 | } 160 | return 161 | } 162 | 163 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScheduledScalerStep. 164 | func (in *ScheduledScalerStep) DeepCopy() *ScheduledScalerStep { 165 | if in == nil { 166 | return nil 167 | } 168 | out := new(ScheduledScalerStep) 169 | in.DeepCopyInto(out) 170 | return out 171 | } 172 | 173 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 174 | func (in *ScheduledScalerTarget) DeepCopyInto(out *ScheduledScalerTarget) { 175 | *out = *in 176 | return 177 | } 178 | 179 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScheduledScalerTarget. 180 | func (in *ScheduledScalerTarget) DeepCopy() *ScheduledScalerTarget { 181 | if in == nil { 182 | return nil 183 | } 184 | out := new(ScheduledScalerTarget) 185 | in.DeepCopyInto(out) 186 | return out 187 | } 188 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/scaling/v1alpha1/scheduledscaler.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | //go:generate mockgen -destination=mock_$GOPACKAGE/$GOFILE k8s.restdev.com/operators/pkg/client/clientset/versioned/typed/scaling/v1alpha1 ScheduledScalersGetter,ScheduledScalerInterface 20 | 21 | import ( 22 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 | types "k8s.io/apimachinery/pkg/types" 24 | watch "k8s.io/apimachinery/pkg/watch" 25 | rest "k8s.io/client-go/rest" 26 | v1alpha1 "k8s.restdev.com/operators/pkg/apis/scaling/v1alpha1" 27 | scheme "k8s.restdev.com/operators/pkg/client/clientset/versioned/scheme" 28 | ) 29 | 30 | // ScheduledScalersGetter has a method to return a ScheduledScalerInterface. 31 | // A group's client should implement this interface. 32 | type ScheduledScalersGetter interface { 33 | ScheduledScalers(namespace string) ScheduledScalerInterface 34 | } 35 | 36 | // ScheduledScalerInterface has methods to work with ScheduledScaler resources. 37 | type ScheduledScalerInterface interface { 38 | Create(*v1alpha1.ScheduledScaler) (*v1alpha1.ScheduledScaler, error) 39 | Update(*v1alpha1.ScheduledScaler) (*v1alpha1.ScheduledScaler, error) 40 | Delete(name string, options *v1.DeleteOptions) error 41 | DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error 42 | Get(name string, options v1.GetOptions) (*v1alpha1.ScheduledScaler, error) 43 | List(opts v1.ListOptions) (*v1alpha1.ScheduledScalerList, error) 44 | Watch(opts v1.ListOptions) (watch.Interface, error) 45 | Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ScheduledScaler, err error) 46 | ScheduledScalerExpansion 47 | } 48 | 49 | // scheduledScalers implements ScheduledScalerInterface 50 | type scheduledScalers struct { 51 | client rest.Interface 52 | ns string 53 | } 54 | 55 | // newScheduledScalers returns a ScheduledScalers 56 | func newScheduledScalers(c *ScalingV1alpha1Client, namespace string) *scheduledScalers { 57 | return &scheduledScalers{ 58 | client: c.RESTClient(), 59 | ns: namespace, 60 | } 61 | } 62 | 63 | // Get takes name of the scheduledScaler, and returns the corresponding scheduledScaler object, and an error if there is any. 64 | func (c *scheduledScalers) Get(name string, options v1.GetOptions) (result *v1alpha1.ScheduledScaler, err error) { 65 | result = &v1alpha1.ScheduledScaler{} 66 | err = c.client.Get(). 67 | Namespace(c.ns). 68 | Resource("scheduledscalers"). 69 | Name(name). 70 | VersionedParams(&options, scheme.ParameterCodec). 71 | Do(). 72 | Into(result) 73 | return 74 | } 75 | 76 | // List takes label and field selectors, and returns the list of ScheduledScalers that match those selectors. 77 | func (c *scheduledScalers) List(opts v1.ListOptions) (result *v1alpha1.ScheduledScalerList, err error) { 78 | result = &v1alpha1.ScheduledScalerList{} 79 | err = c.client.Get(). 80 | Namespace(c.ns). 81 | Resource("scheduledscalers"). 82 | VersionedParams(&opts, scheme.ParameterCodec). 83 | Do(). 84 | Into(result) 85 | return 86 | } 87 | 88 | // Watch returns a watch.Interface that watches the requested scheduledScalers. 89 | func (c *scheduledScalers) Watch(opts v1.ListOptions) (watch.Interface, error) { 90 | opts.Watch = true 91 | return c.client.Get(). 92 | Namespace(c.ns). 93 | Resource("scheduledscalers"). 94 | VersionedParams(&opts, scheme.ParameterCodec). 95 | Watch() 96 | } 97 | 98 | // Create takes the representation of a scheduledScaler and creates it. Returns the server's representation of the scheduledScaler, and an error, if there is any. 99 | func (c *scheduledScalers) Create(scheduledScaler *v1alpha1.ScheduledScaler) (result *v1alpha1.ScheduledScaler, err error) { 100 | result = &v1alpha1.ScheduledScaler{} 101 | err = c.client.Post(). 102 | Namespace(c.ns). 103 | Resource("scheduledscalers"). 104 | Body(scheduledScaler). 105 | Do(). 106 | Into(result) 107 | return 108 | } 109 | 110 | // Update takes the representation of a scheduledScaler and updates it. Returns the server's representation of the scheduledScaler, and an error, if there is any. 111 | func (c *scheduledScalers) Update(scheduledScaler *v1alpha1.ScheduledScaler) (result *v1alpha1.ScheduledScaler, err error) { 112 | result = &v1alpha1.ScheduledScaler{} 113 | err = c.client.Put(). 114 | Namespace(c.ns). 115 | Resource("scheduledscalers"). 116 | Name(scheduledScaler.Name). 117 | Body(scheduledScaler). 118 | Do(). 119 | Into(result) 120 | return 121 | } 122 | 123 | // Delete takes name of the scheduledScaler and deletes it. Returns an error if one occurs. 124 | func (c *scheduledScalers) Delete(name string, options *v1.DeleteOptions) error { 125 | return c.client.Delete(). 126 | Namespace(c.ns). 127 | Resource("scheduledscalers"). 128 | Name(name). 129 | Body(options). 130 | Do(). 131 | Error() 132 | } 133 | 134 | // DeleteCollection deletes a collection of objects. 135 | func (c *scheduledScalers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { 136 | return c.client.Delete(). 137 | Namespace(c.ns). 138 | Resource("scheduledscalers"). 139 | VersionedParams(&listOptions, scheme.ParameterCodec). 140 | Body(options). 141 | Do(). 142 | Error() 143 | } 144 | 145 | // Patch applies the patch and returns the patched scheduledScaler. 146 | func (c *scheduledScalers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ScheduledScaler, err error) { 147 | result = &v1alpha1.ScheduledScaler{} 148 | err = c.client.Patch(pt). 149 | Namespace(c.ns). 150 | Resource("scheduledscalers"). 151 | SubResource(subresources...). 152 | Name(name). 153 | Body(data). 154 | Do(). 155 | Into(result) 156 | return 157 | } 158 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scheduled Scaler 2 | 3 | [![All Contributors](https://img.shields.io/badge/all_contributors-4-orange.svg?style=flat-square)](#contributors-) 4 | 5 | [![Docker Image Version (latest semver)](https://img.shields.io/docker/v/k8srestdev/scaling?style=for-the-badge)](https://hub.docker.com/repository/docker/k8srestdev/scaling) [![Travis (.com) branch](https://img.shields.io/travis/com/West-Coast-Devops/scheduled-scaler/master?style=for-the-badge)](https://travis-ci.com/github/West-Coast-Devops/scheduled-scaler) 6 | 7 | In order to use the ScheduledScaler you will need to install the CRD and deploy the Scaling Controller into your Kubernetes cluster. 8 | 9 | ## Requirements 10 | 11 | * Kubernetes Version: 1.7+ 12 | * Kubernetes Cluster Settings: 13 | * "Legacy authorization": "Enabled" 14 | 15 | ## Tested Environments 16 | 17 | * Google Kubernetes Engine 18 | * Kubernetes Version: 1.9.3-gke.0, 1.7.15 19 | * Docker Version: 1.12.5 20 | * Golang Version: 1.9.4 21 | 22 | ## Getting Started 23 | 24 | **Clone this repo** 25 | ``` 26 | mkdir -p $GOPATH/src/k8s.restdev.com && \ 27 | git clone https://github.com/k8s-restdev/scheduled-scaler.git $GOPATH/src/k8s.restdev.com/operators && \ 28 | cd $GOPATH/src/k8s.restdev.com/operators 29 | ``` 30 | 31 | **Install using Helm Chart** 32 | ```bash 33 | helm install scheduled-scaler artifacts/kubes/scaling/chart 34 | ``` 35 | 36 | > **Note**: This uses the image stored at https://hub.docker.com/r/k8srestdev/scaling by default. 37 | 38 | [See chart README](artifacts/kubes/scaling/chart) for detailed configuration options 39 | 40 | **Installation without Helm (and compiling binary yourself):** 41 | 42 | 1. Install the CRD 43 | ``` 44 | kubectl create -f ./artifacts/kubes/scaling/crd.yml 45 | ``` 46 | 2. Once you have the repo installed on your local dev you can test, build, push and deploy using `make` 47 | 48 | > **Note**: If you are just looking for a prebuilt image you can find the latest build [here](https://hub.docker.com/r/k8srestdev/scaling/). 49 | > Just add that image tag to the deployment yml in the artificats dir and apply to your `kube-system` namespace to get up and running without doing a fresh build :D 50 | 51 | 52 | ### Using Make 53 | The `Makefile` provides the following steps: 54 | 1. test - Run go unit tests 55 | 2. build - Build the go bin file and docker image locally 56 | 3. push - Push the built docker image to gcr (or another repository of your choice) 57 | 4. deploy - Deploy the updated image to your Kubernetes cluster 58 | 59 | Each of these steps can be run in a single pass or can be used individually. 60 | 61 | **Examples** 62 | 63 | - Do all the things (kubectl) 64 | ``` 65 | # This example will test, build, push and deploy using kubectl's currently configured cluster 66 | make OPERATOR=scaling PROJECT_ID=my_project_id 67 | ``` 68 | 69 | - Do all the things (kubernodes) 70 | ``` 71 | # This example will test, build, push and deploy using kubernodes 72 | make OPERATOR=scaling PROJECT_ID=my_project_id DEPLOYBIN=kn KN_PROJECT_ID=my_kubernodes_project_id 73 | ``` 74 | > **Note:** You only need to add `KN_PROJECT_ID` if it differs from `PROJECT_ID` 75 | 76 | - Just build the image 77 | ``` 78 | make build OPERATOR=scaling PROJECT_ID=my_project_id 79 | ``` 80 | 81 | - Just push any image 82 | ``` 83 | make push IMAGE=myrepo/myimage:mytag 84 | ``` 85 | 86 | - Just deploy any image (kubectl) 87 | ``` 88 | make deploy OPERATOR=scaling IMAGE=myrepo/myimage:mytag 89 | ``` 90 | 91 | - Just deploy any image (kubernodes) 92 | ``` 93 | make deploy OPERATOR=scaling IMAGE=myrepo/myimage:mytag DEPLOYBIN=kn KN_PROJECT_ID=my_kubernodes_project_id 94 | ``` 95 | 96 | Now that you have all the resources required in your cluster you can begin creating ScheduledScalers. 97 | 98 | ## Scheduled Scaler Spec 99 | 100 | > **Note:** This controller uses the following [Cron Expression Format](https://godoc.org/github.com/robfig/cron#hdr-CRON_Expression_Format) 101 | 102 | ### HPA 103 | 104 | ```yaml 105 | apiVersion: "scaling.k8s.restdev.com/v1alpha1" 106 | kind: ScheduledScaler 107 | metadata: 108 | name: my-scheduled-scaler-1 109 | spec: 110 | timeZone: America/Los_Angeles 111 | target: 112 | kind: HorizontalPodAutoscaler 113 | name: my-hpa 114 | apiVersion: autoscaling/v1 115 | steps: 116 | #run at 5:30am PST 117 | - runat: '0 30 5 * * *' 118 | mode: range 119 | minReplicas: 1 120 | maxReplicas: 5 121 | ``` 122 | 123 | ### Instance Group 124 | 125 | ```yaml 126 | apiVersion: "scaling.k8s.restdev.com/v1alpha1" 127 | kind: ScheduledScaler 128 | metadata: 129 | name: my-scheduled-scaler-2 130 | spec: 131 | timeZone: America/Los_Angeles 132 | target: 133 | kind: InstanceGroup 134 | name: my-instance-group-name 135 | apiVersion: compute/v1 136 | steps: 137 | #run at 5:30am PST 138 | - runat: '0 30 5 * * *' 139 | mode: fixed 140 | replicas: 3 141 | ``` 142 | 143 | As you'll see above, you can target either instance groups (if you are on GKE) or hpa, but all the other options are the same. 144 | 145 | ## Options 146 | 147 | | Option | Description | Required | 148 | |--|--|--| 149 | | spec.timeZone | Timezone to run crons in | False | 150 | | spec.target.kind | Type of target (InstanceGroup/HorizontalPodAutoscaler) | True 151 | | spec.target.name | Name of the target resource | True 152 | | spec.target.apiVersion | API Version of the target | True 153 | | spec.steps | List of steps | True 154 | | spec.steps[].runat | Cronjob time string (gocron) | True 155 | | spec.steps[].mode | Type of scaling to run (fixed/range) | True 156 | | spec.steps[].replicas | Defined if mode is 'fixed' | False 157 | | spec.steps[].minReplicas | Defined if mode is 'range' | False 158 | | spec.steps[].maxReplicas | Defined if mode is 'range' | False 159 | 160 | For more details on how this add-on can be used please follow the link below: 161 | [Learn More...](http://k8s.restdev.com/p/scheduled-scaler.html) 162 | 163 | ## Contributors ✨ 164 | 165 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 |

Sheridan C Rawlins

🚧 ⚠️ 💻

vnandha

🐛 🚧

so0k

🚇 📖

Andreas Wunderlich

📖
178 | 179 | 180 | 181 | 182 | 183 | 184 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/scaling/v1alpha1/mock_v1alpha1/scheduledscaler.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: k8s.restdev.com/operators/pkg/client/clientset/versioned/typed/scaling/v1alpha1 (interfaces: ScheduledScalersGetter,ScheduledScalerInterface) 3 | 4 | // Package mock_v1alpha1 is a generated GoMock package. 5 | package mock_v1alpha1 6 | 7 | import ( 8 | gomock "github.com/golang/mock/gomock" 9 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | types "k8s.io/apimachinery/pkg/types" 11 | watch "k8s.io/apimachinery/pkg/watch" 12 | v1alpha1 "k8s.restdev.com/operators/pkg/apis/scaling/v1alpha1" 13 | v1alpha10 "k8s.restdev.com/operators/pkg/client/clientset/versioned/typed/scaling/v1alpha1" 14 | reflect "reflect" 15 | ) 16 | 17 | // MockScheduledScalersGetter is a mock of ScheduledScalersGetter interface 18 | type MockScheduledScalersGetter struct { 19 | ctrl *gomock.Controller 20 | recorder *MockScheduledScalersGetterMockRecorder 21 | } 22 | 23 | // MockScheduledScalersGetterMockRecorder is the mock recorder for MockScheduledScalersGetter 24 | type MockScheduledScalersGetterMockRecorder struct { 25 | mock *MockScheduledScalersGetter 26 | } 27 | 28 | // NewMockScheduledScalersGetter creates a new mock instance 29 | func NewMockScheduledScalersGetter(ctrl *gomock.Controller) *MockScheduledScalersGetter { 30 | mock := &MockScheduledScalersGetter{ctrl: ctrl} 31 | mock.recorder = &MockScheduledScalersGetterMockRecorder{mock} 32 | return mock 33 | } 34 | 35 | // EXPECT returns an object that allows the caller to indicate expected use 36 | func (m *MockScheduledScalersGetter) EXPECT() *MockScheduledScalersGetterMockRecorder { 37 | return m.recorder 38 | } 39 | 40 | // ScheduledScalers mocks base method 41 | func (m *MockScheduledScalersGetter) ScheduledScalers(arg0 string) v1alpha10.ScheduledScalerInterface { 42 | m.ctrl.T.Helper() 43 | ret := m.ctrl.Call(m, "ScheduledScalers", arg0) 44 | ret0, _ := ret[0].(v1alpha10.ScheduledScalerInterface) 45 | return ret0 46 | } 47 | 48 | // ScheduledScalers indicates an expected call of ScheduledScalers 49 | func (mr *MockScheduledScalersGetterMockRecorder) ScheduledScalers(arg0 interface{}) *gomock.Call { 50 | mr.mock.ctrl.T.Helper() 51 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScheduledScalers", reflect.TypeOf((*MockScheduledScalersGetter)(nil).ScheduledScalers), arg0) 52 | } 53 | 54 | // MockScheduledScalerInterface is a mock of ScheduledScalerInterface interface 55 | type MockScheduledScalerInterface struct { 56 | ctrl *gomock.Controller 57 | recorder *MockScheduledScalerInterfaceMockRecorder 58 | } 59 | 60 | // MockScheduledScalerInterfaceMockRecorder is the mock recorder for MockScheduledScalerInterface 61 | type MockScheduledScalerInterfaceMockRecorder struct { 62 | mock *MockScheduledScalerInterface 63 | } 64 | 65 | // NewMockScheduledScalerInterface creates a new mock instance 66 | func NewMockScheduledScalerInterface(ctrl *gomock.Controller) *MockScheduledScalerInterface { 67 | mock := &MockScheduledScalerInterface{ctrl: ctrl} 68 | mock.recorder = &MockScheduledScalerInterfaceMockRecorder{mock} 69 | return mock 70 | } 71 | 72 | // EXPECT returns an object that allows the caller to indicate expected use 73 | func (m *MockScheduledScalerInterface) EXPECT() *MockScheduledScalerInterfaceMockRecorder { 74 | return m.recorder 75 | } 76 | 77 | // Create mocks base method 78 | func (m *MockScheduledScalerInterface) Create(arg0 *v1alpha1.ScheduledScaler) (*v1alpha1.ScheduledScaler, error) { 79 | m.ctrl.T.Helper() 80 | ret := m.ctrl.Call(m, "Create", arg0) 81 | ret0, _ := ret[0].(*v1alpha1.ScheduledScaler) 82 | ret1, _ := ret[1].(error) 83 | return ret0, ret1 84 | } 85 | 86 | // Create indicates an expected call of Create 87 | func (mr *MockScheduledScalerInterfaceMockRecorder) Create(arg0 interface{}) *gomock.Call { 88 | mr.mock.ctrl.T.Helper() 89 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockScheduledScalerInterface)(nil).Create), arg0) 90 | } 91 | 92 | // Delete mocks base method 93 | func (m *MockScheduledScalerInterface) Delete(arg0 string, arg1 *v1.DeleteOptions) error { 94 | m.ctrl.T.Helper() 95 | ret := m.ctrl.Call(m, "Delete", arg0, arg1) 96 | ret0, _ := ret[0].(error) 97 | return ret0 98 | } 99 | 100 | // Delete indicates an expected call of Delete 101 | func (mr *MockScheduledScalerInterfaceMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { 102 | mr.mock.ctrl.T.Helper() 103 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockScheduledScalerInterface)(nil).Delete), arg0, arg1) 104 | } 105 | 106 | // DeleteCollection mocks base method 107 | func (m *MockScheduledScalerInterface) DeleteCollection(arg0 *v1.DeleteOptions, arg1 v1.ListOptions) error { 108 | m.ctrl.T.Helper() 109 | ret := m.ctrl.Call(m, "DeleteCollection", arg0, arg1) 110 | ret0, _ := ret[0].(error) 111 | return ret0 112 | } 113 | 114 | // DeleteCollection indicates an expected call of DeleteCollection 115 | func (mr *MockScheduledScalerInterfaceMockRecorder) DeleteCollection(arg0, arg1 interface{}) *gomock.Call { 116 | mr.mock.ctrl.T.Helper() 117 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCollection", reflect.TypeOf((*MockScheduledScalerInterface)(nil).DeleteCollection), arg0, arg1) 118 | } 119 | 120 | // Get mocks base method 121 | func (m *MockScheduledScalerInterface) Get(arg0 string, arg1 v1.GetOptions) (*v1alpha1.ScheduledScaler, error) { 122 | m.ctrl.T.Helper() 123 | ret := m.ctrl.Call(m, "Get", arg0, arg1) 124 | ret0, _ := ret[0].(*v1alpha1.ScheduledScaler) 125 | ret1, _ := ret[1].(error) 126 | return ret0, ret1 127 | } 128 | 129 | // Get indicates an expected call of Get 130 | func (mr *MockScheduledScalerInterfaceMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call { 131 | mr.mock.ctrl.T.Helper() 132 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockScheduledScalerInterface)(nil).Get), arg0, arg1) 133 | } 134 | 135 | // List mocks base method 136 | func (m *MockScheduledScalerInterface) List(arg0 v1.ListOptions) (*v1alpha1.ScheduledScalerList, error) { 137 | m.ctrl.T.Helper() 138 | ret := m.ctrl.Call(m, "List", arg0) 139 | ret0, _ := ret[0].(*v1alpha1.ScheduledScalerList) 140 | ret1, _ := ret[1].(error) 141 | return ret0, ret1 142 | } 143 | 144 | // List indicates an expected call of List 145 | func (mr *MockScheduledScalerInterfaceMockRecorder) List(arg0 interface{}) *gomock.Call { 146 | mr.mock.ctrl.T.Helper() 147 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockScheduledScalerInterface)(nil).List), arg0) 148 | } 149 | 150 | // Patch mocks base method 151 | func (m *MockScheduledScalerInterface) Patch(arg0 string, arg1 types.PatchType, arg2 []byte, arg3 ...string) (*v1alpha1.ScheduledScaler, error) { 152 | m.ctrl.T.Helper() 153 | varargs := []interface{}{arg0, arg1, arg2} 154 | for _, a := range arg3 { 155 | varargs = append(varargs, a) 156 | } 157 | ret := m.ctrl.Call(m, "Patch", varargs...) 158 | ret0, _ := ret[0].(*v1alpha1.ScheduledScaler) 159 | ret1, _ := ret[1].(error) 160 | return ret0, ret1 161 | } 162 | 163 | // Patch indicates an expected call of Patch 164 | func (mr *MockScheduledScalerInterfaceMockRecorder) Patch(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { 165 | mr.mock.ctrl.T.Helper() 166 | varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) 167 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockScheduledScalerInterface)(nil).Patch), varargs...) 168 | } 169 | 170 | // Update mocks base method 171 | func (m *MockScheduledScalerInterface) Update(arg0 *v1alpha1.ScheduledScaler) (*v1alpha1.ScheduledScaler, error) { 172 | m.ctrl.T.Helper() 173 | ret := m.ctrl.Call(m, "Update", arg0) 174 | ret0, _ := ret[0].(*v1alpha1.ScheduledScaler) 175 | ret1, _ := ret[1].(error) 176 | return ret0, ret1 177 | } 178 | 179 | // Update indicates an expected call of Update 180 | func (mr *MockScheduledScalerInterfaceMockRecorder) Update(arg0 interface{}) *gomock.Call { 181 | mr.mock.ctrl.T.Helper() 182 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockScheduledScalerInterface)(nil).Update), arg0) 183 | } 184 | 185 | // Watch mocks base method 186 | func (m *MockScheduledScalerInterface) Watch(arg0 v1.ListOptions) (watch.Interface, error) { 187 | m.ctrl.T.Helper() 188 | ret := m.ctrl.Call(m, "Watch", arg0) 189 | ret0, _ := ret[0].(watch.Interface) 190 | ret1, _ := ret[1].(error) 191 | return ret0, ret1 192 | } 193 | 194 | // Watch indicates an expected call of Watch 195 | func (mr *MockScheduledScalerInterfaceMockRecorder) Watch(arg0 interface{}) *gomock.Call { 196 | mr.mock.ctrl.T.Helper() 197 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockScheduledScalerInterface)(nil).Watch), arg0) 198 | } 199 | -------------------------------------------------------------------------------- /scaling-controller_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golang/mock/gomock" 7 | "github.com/robfig/cron" 8 | "github.com/stretchr/testify/require" 9 | v1 "k8s.io/api/autoscaling/v1" 10 | "k8s.io/apimachinery/pkg/api/errors" 11 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | "k8s.io/apimachinery/pkg/runtime/schema" 13 | scalingv1alpha1 "k8s.restdev.com/operators/pkg/apis/scaling/v1alpha1" 14 | "k8s.restdev.com/operators/pkg/client/clientset/versioned/mock_versioned" 15 | mock_v1alpha12 "k8s.restdev.com/operators/pkg/client/clientset/versioned/typed/scaling/v1alpha1/mock_v1alpha1" 16 | "k8s.restdev.com/operators/pkg/client/listers/scaling/v1alpha1/mock_v1alpha1" 17 | cron2 "k8s.restdev.com/operators/pkg/services/scaling/cron" 18 | "k8s.restdev.com/operators/pkg/services/scaling/cron/mock_cron" 19 | mock_external "k8s.restdev.com/operators/test/external" 20 | ) 21 | 22 | func Test_validateScheduledScaler(t *testing.T) { 23 | tests := []struct { 24 | name string 25 | scheduledScaler *scalingv1alpha1.ScheduledScaler 26 | wantErr bool 27 | }{ 28 | { 29 | name: `nil scheduledScaler`, 30 | wantErr: true, 31 | }, 32 | { 33 | name: `Runat with too few fields`, 34 | scheduledScaler: &scalingv1alpha1.ScheduledScaler{ 35 | Spec: scalingv1alpha1.ScheduledScalerSpec{ 36 | Steps: []scalingv1alpha1.ScheduledScalerStep{ 37 | { 38 | Runat: "1 2 3", 39 | }, 40 | }, 41 | }, 42 | }, 43 | wantErr: true, 44 | }, 45 | { 46 | name: `Runat with February 30`, 47 | scheduledScaler: &scalingv1alpha1.ScheduledScaler{ 48 | Spec: scalingv1alpha1.ScheduledScalerSpec{ 49 | Steps: []scalingv1alpha1.ScheduledScalerStep{ 50 | { 51 | Runat: "0 0 6 30 FEB *", 52 | }, 53 | }, 54 | }, 55 | }, 56 | wantErr: true, 57 | }, 58 | { 59 | name: `valid date is ok ("0 0 6 * * SAT")`, 60 | scheduledScaler: &scalingv1alpha1.ScheduledScaler{ 61 | Spec: scalingv1alpha1.ScheduledScalerSpec{ 62 | Steps: []scalingv1alpha1.ScheduledScalerStep{ 63 | { 64 | Runat: "0 0 6 * * SAT", 65 | }, 66 | }, 67 | }, 68 | }, 69 | }, 70 | } 71 | for _, tt := range tests { 72 | t.Run(tt.name, func(t *testing.T) { 73 | c := &ScheduledScalerController{ 74 | cronProxy: new(cron2.CronImpl), 75 | } 76 | err := c.validateScheduledScaler(tt.scheduledScaler) 77 | if tt.wantErr { 78 | require.Error(t, err) 79 | return 80 | } 81 | require.NoError(t, err) 82 | }) 83 | } 84 | } 85 | 86 | func newInt32(i int32) *int32 { 87 | return &i 88 | } 89 | 90 | func TestScheduledScalerController_scheduledScalerHpaCronAdd(t *testing.T) { 91 | testHPA := &v1.HorizontalPodAutoscaler{ 92 | Spec: v1.HorizontalPodAutoscalerSpec{ 93 | MinReplicas: newInt32(10), 94 | MaxReplicas: 20, 95 | }, 96 | } 97 | testSS := scalingv1alpha1.ScheduledScaler{ 98 | Spec: scalingv1alpha1.ScheduledScalerSpec{ 99 | Steps: []scalingv1alpha1.ScheduledScalerStep{ 100 | { 101 | Mode: "range", 102 | Runat: "0 0 6 * * SAT", 103 | MinReplicas: newInt32(100), 104 | MaxReplicas: newInt32(200), 105 | }, 106 | }, 107 | }, 108 | } 109 | tests := []struct { 110 | name string 111 | ss scalingv1alpha1.ScheduledScaler 112 | maxRetries int 113 | ssListerGetErr error 114 | hpaGetResults []*v1.HorizontalPodAutoscaler 115 | hpaGetErrs []error 116 | hpaUpdatesErrs []error 117 | ssGetResults []*scalingv1alpha1.ScheduledScaler 118 | ssGetErrs []error 119 | ssUpdateErrs []error 120 | }{ 121 | { 122 | name: "empty SS", 123 | hpaGetResults: []*v1.HorizontalPodAutoscaler{ 124 | nil, 125 | }, 126 | hpaGetErrs: []error{ 127 | nil, 128 | }, 129 | }, 130 | { 131 | name: "SS with one spec, fails once and retries", 132 | ss: testSS, 133 | maxRetries: 3, 134 | hpaGetResults: []*v1.HorizontalPodAutoscaler{ 135 | testHPA, 136 | testHPA, 137 | testHPA, 138 | }, 139 | hpaGetErrs: []error{ 140 | nil, 141 | nil, 142 | nil, 143 | }, 144 | hpaUpdatesErrs: []error{ 145 | errors.NewConflict(schema.GroupResource{}, "foo", nil), 146 | nil, 147 | }, 148 | ssUpdateErrs: []error{ 149 | nil, 150 | }, 151 | ssGetResults: []*scalingv1alpha1.ScheduledScaler{ 152 | &testSS, 153 | }, 154 | ssGetErrs: []error{ 155 | nil, 156 | }, 157 | }, 158 | { 159 | name: "SS with one spec, fails ss update once and retries", 160 | ss: testSS, 161 | maxRetries: 3, 162 | hpaGetResults: []*v1.HorizontalPodAutoscaler{ 163 | testHPA, 164 | testHPA, 165 | }, 166 | hpaGetErrs: []error{ 167 | nil, 168 | nil, 169 | }, 170 | hpaUpdatesErrs: []error{ 171 | nil, 172 | }, 173 | ssUpdateErrs: []error{ 174 | errors.NewConflict(schema.GroupResource{}, "foo", nil), 175 | nil, 176 | }, 177 | ssGetResults: []*scalingv1alpha1.ScheduledScaler{ 178 | &testSS, 179 | &testSS, 180 | }, 181 | ssGetErrs: []error{ 182 | nil, 183 | nil, 184 | }, 185 | }, 186 | { 187 | name: "SS with one spec, fails more than maxRetries; never calls ss update", 188 | ss: testSS, 189 | maxRetries: 3, 190 | hpaGetResults: []*v1.HorizontalPodAutoscaler{ 191 | testHPA, 192 | testHPA, 193 | testHPA, 194 | testHPA, 195 | testHPA, 196 | }, 197 | hpaGetErrs: []error{ 198 | nil, 199 | nil, 200 | nil, 201 | nil, 202 | nil, 203 | }, 204 | hpaUpdatesErrs: []error{ 205 | errors.NewConflict(schema.GroupResource{}, "foo", nil), 206 | errors.NewConflict(schema.GroupResource{}, "foo", nil), 207 | errors.NewConflict(schema.GroupResource{}, "foo", nil), 208 | errors.NewConflict(schema.GroupResource{}, "foo", nil), 209 | }, 210 | }, 211 | } 212 | for _, tt := range tests { 213 | t.Run(tt.name, func(t *testing.T) { 214 | ctrl := gomock.NewController(t) 215 | defer ctrl.Finish() 216 | 217 | mockCronProxy := mock_cron.NewMockCronProxy(ctrl) 218 | mockCronProxy.EXPECT(). 219 | Create(gomock.Any()). 220 | DoAndReturn(func(tz string) (*cron.Cron, error) { 221 | return cron.New(), nil 222 | }) 223 | var calls []func() 224 | for _, step := range tt.ss.Spec.Steps { 225 | mockCronProxy.EXPECT(). 226 | Push(gomock.Any(), gomock.Eq(step.Runat), gomock.Any()). 227 | Do(func(c *cron.Cron, time string, call func()) { 228 | calls = append(calls, call) 229 | }) 230 | } 231 | mockCronProxy.EXPECT(). 232 | Start(gomock.Any()). 233 | Do(func(c *cron.Cron) { 234 | for _, call := range calls { 235 | call() 236 | } 237 | }) 238 | 239 | mockNsLister := mock_v1alpha1.NewMockScheduledScalerNamespaceLister(ctrl) 240 | mockNsLister.EXPECT(). 241 | Get(tt.ss.Name). 242 | Return(&tt.ss, tt.ssListerGetErr) 243 | mockLister := mock_v1alpha1.NewMockScheduledScalerLister(ctrl) 244 | mockLister.EXPECT(). 245 | ScheduledScalers(tt.ss.Namespace). 246 | Return(mockNsLister) 247 | 248 | mockHPA := mock_external.NewMockHorizontalPodAutoscalerInterface(ctrl) 249 | getCallIndex := 0 250 | mockHPA.EXPECT(). 251 | Get(gomock.Any(), gomock.Any()). 252 | Times(len(tt.hpaGetResults)). 253 | DoAndReturn(func(name string, options metav1.GetOptions) (*v1.HorizontalPodAutoscaler, error) { 254 | result := tt.hpaGetResults[getCallIndex] 255 | err := tt.hpaGetErrs[getCallIndex] 256 | getCallIndex++ 257 | return result, err 258 | }) 259 | updateCallIndex := 0 260 | mockHPA.EXPECT(). 261 | Update(gomock.Any()). 262 | Times(len(tt.hpaUpdatesErrs)). 263 | DoAndReturn(func(input *v1.HorizontalPodAutoscaler) (*v1.HorizontalPodAutoscaler, error) { 264 | err := tt.hpaUpdatesErrs[updateCallIndex] 265 | updateCallIndex++ 266 | return input, err 267 | }) 268 | mockAutoscaling := mock_external.NewMockAutoscalingV1Interface(ctrl) 269 | mockAutoscaling.EXPECT(). 270 | HorizontalPodAutoscalers(tt.ss.Namespace). 271 | Return(mockHPA) 272 | mockKubeClient := mock_external.NewMockInterface(ctrl) 273 | mockKubeClient.EXPECT(). 274 | AutoscalingV1(). 275 | Return(mockAutoscaling) 276 | 277 | mockScheduledScalerInterface := mock_v1alpha12.NewMockScheduledScalerInterface(ctrl) 278 | ssGetIndex := 0 279 | mockScheduledScalerInterface.EXPECT(). 280 | Get(gomock.Any(), gomock.Any()). 281 | Times(len(tt.ssGetResults)). 282 | DoAndReturn(func(name string, options metav1.GetOptions) (*scalingv1alpha1.ScheduledScaler, error) { 283 | result := tt.ssGetResults[ssGetIndex] 284 | err := tt.ssGetErrs[ssGetIndex] 285 | ssGetIndex++ 286 | return result, err 287 | }) 288 | 289 | ssUpdateIndex := 0 290 | mockScheduledScalerInterface.EXPECT(). 291 | Update(gomock.Any()). 292 | Times(len(tt.ssUpdateErrs)). 293 | DoAndReturn(func(*scalingv1alpha1.ScheduledScaler) (*scalingv1alpha1.ScheduledScaler, error) { 294 | err := tt.ssUpdateErrs[ssUpdateIndex] 295 | ssUpdateIndex++ 296 | return nil, err 297 | }) 298 | mockScalingV1alpha1Interface := mock_v1alpha12.NewMockScalingV1alpha1Interface(ctrl) 299 | mockScalingV1alpha1Interface.EXPECT(). 300 | ScheduledScalers(tt.ss.Namespace). 301 | Return(mockScheduledScalerInterface) 302 | mockRestdevClient := mock_versioned.NewMockInterface(ctrl) 303 | mockRestdevClient.EXPECT(). 304 | ScalingV1alpha1(). 305 | Return(mockScalingV1alpha1Interface) 306 | c := &ScheduledScalerController{ 307 | maxRetries: tt.maxRetries, 308 | cronProxy: mockCronProxy, 309 | restdevClient: mockRestdevClient, 310 | kubeClient: mockKubeClient, 311 | scheduledScalersLister: mockLister, 312 | } 313 | c.scheduledScalerHpaCronAdd(&tt.ss) 314 | }) 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /test/external/autoscalingv1.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: k8s.io/client-go/kubernetes/typed/autoscaling/v1 (interfaces: AutoscalingV1Interface,HorizontalPodAutoscalerInterface) 3 | 4 | // Package mock_external is a generated GoMock package. 5 | package mock_external 6 | 7 | import ( 8 | gomock "github.com/golang/mock/gomock" 9 | v1 "k8s.io/api/autoscaling/v1" 10 | v10 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | types "k8s.io/apimachinery/pkg/types" 12 | watch "k8s.io/apimachinery/pkg/watch" 13 | v11 "k8s.io/client-go/kubernetes/typed/autoscaling/v1" 14 | rest "k8s.io/client-go/rest" 15 | reflect "reflect" 16 | ) 17 | 18 | // MockAutoscalingV1Interface is a mock of AutoscalingV1Interface interface 19 | type MockAutoscalingV1Interface struct { 20 | ctrl *gomock.Controller 21 | recorder *MockAutoscalingV1InterfaceMockRecorder 22 | } 23 | 24 | // MockAutoscalingV1InterfaceMockRecorder is the mock recorder for MockAutoscalingV1Interface 25 | type MockAutoscalingV1InterfaceMockRecorder struct { 26 | mock *MockAutoscalingV1Interface 27 | } 28 | 29 | // NewMockAutoscalingV1Interface creates a new mock instance 30 | func NewMockAutoscalingV1Interface(ctrl *gomock.Controller) *MockAutoscalingV1Interface { 31 | mock := &MockAutoscalingV1Interface{ctrl: ctrl} 32 | mock.recorder = &MockAutoscalingV1InterfaceMockRecorder{mock} 33 | return mock 34 | } 35 | 36 | // EXPECT returns an object that allows the caller to indicate expected use 37 | func (m *MockAutoscalingV1Interface) EXPECT() *MockAutoscalingV1InterfaceMockRecorder { 38 | return m.recorder 39 | } 40 | 41 | // HorizontalPodAutoscalers mocks base method 42 | func (m *MockAutoscalingV1Interface) HorizontalPodAutoscalers(arg0 string) v11.HorizontalPodAutoscalerInterface { 43 | m.ctrl.T.Helper() 44 | ret := m.ctrl.Call(m, "HorizontalPodAutoscalers", arg0) 45 | ret0, _ := ret[0].(v11.HorizontalPodAutoscalerInterface) 46 | return ret0 47 | } 48 | 49 | // HorizontalPodAutoscalers indicates an expected call of HorizontalPodAutoscalers 50 | func (mr *MockAutoscalingV1InterfaceMockRecorder) HorizontalPodAutoscalers(arg0 interface{}) *gomock.Call { 51 | mr.mock.ctrl.T.Helper() 52 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HorizontalPodAutoscalers", reflect.TypeOf((*MockAutoscalingV1Interface)(nil).HorizontalPodAutoscalers), arg0) 53 | } 54 | 55 | // RESTClient mocks base method 56 | func (m *MockAutoscalingV1Interface) RESTClient() rest.Interface { 57 | m.ctrl.T.Helper() 58 | ret := m.ctrl.Call(m, "RESTClient") 59 | ret0, _ := ret[0].(rest.Interface) 60 | return ret0 61 | } 62 | 63 | // RESTClient indicates an expected call of RESTClient 64 | func (mr *MockAutoscalingV1InterfaceMockRecorder) RESTClient() *gomock.Call { 65 | mr.mock.ctrl.T.Helper() 66 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RESTClient", reflect.TypeOf((*MockAutoscalingV1Interface)(nil).RESTClient)) 67 | } 68 | 69 | // MockHorizontalPodAutoscalerInterface is a mock of HorizontalPodAutoscalerInterface interface 70 | type MockHorizontalPodAutoscalerInterface struct { 71 | ctrl *gomock.Controller 72 | recorder *MockHorizontalPodAutoscalerInterfaceMockRecorder 73 | } 74 | 75 | // MockHorizontalPodAutoscalerInterfaceMockRecorder is the mock recorder for MockHorizontalPodAutoscalerInterface 76 | type MockHorizontalPodAutoscalerInterfaceMockRecorder struct { 77 | mock *MockHorizontalPodAutoscalerInterface 78 | } 79 | 80 | // NewMockHorizontalPodAutoscalerInterface creates a new mock instance 81 | func NewMockHorizontalPodAutoscalerInterface(ctrl *gomock.Controller) *MockHorizontalPodAutoscalerInterface { 82 | mock := &MockHorizontalPodAutoscalerInterface{ctrl: ctrl} 83 | mock.recorder = &MockHorizontalPodAutoscalerInterfaceMockRecorder{mock} 84 | return mock 85 | } 86 | 87 | // EXPECT returns an object that allows the caller to indicate expected use 88 | func (m *MockHorizontalPodAutoscalerInterface) EXPECT() *MockHorizontalPodAutoscalerInterfaceMockRecorder { 89 | return m.recorder 90 | } 91 | 92 | // Create mocks base method 93 | func (m *MockHorizontalPodAutoscalerInterface) Create(arg0 *v1.HorizontalPodAutoscaler) (*v1.HorizontalPodAutoscaler, error) { 94 | m.ctrl.T.Helper() 95 | ret := m.ctrl.Call(m, "Create", arg0) 96 | ret0, _ := ret[0].(*v1.HorizontalPodAutoscaler) 97 | ret1, _ := ret[1].(error) 98 | return ret0, ret1 99 | } 100 | 101 | // Create indicates an expected call of Create 102 | func (mr *MockHorizontalPodAutoscalerInterfaceMockRecorder) Create(arg0 interface{}) *gomock.Call { 103 | mr.mock.ctrl.T.Helper() 104 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockHorizontalPodAutoscalerInterface)(nil).Create), arg0) 105 | } 106 | 107 | // Delete mocks base method 108 | func (m *MockHorizontalPodAutoscalerInterface) Delete(arg0 string, arg1 *v10.DeleteOptions) error { 109 | m.ctrl.T.Helper() 110 | ret := m.ctrl.Call(m, "Delete", arg0, arg1) 111 | ret0, _ := ret[0].(error) 112 | return ret0 113 | } 114 | 115 | // Delete indicates an expected call of Delete 116 | func (mr *MockHorizontalPodAutoscalerInterfaceMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { 117 | mr.mock.ctrl.T.Helper() 118 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockHorizontalPodAutoscalerInterface)(nil).Delete), arg0, arg1) 119 | } 120 | 121 | // DeleteCollection mocks base method 122 | func (m *MockHorizontalPodAutoscalerInterface) DeleteCollection(arg0 *v10.DeleteOptions, arg1 v10.ListOptions) error { 123 | m.ctrl.T.Helper() 124 | ret := m.ctrl.Call(m, "DeleteCollection", arg0, arg1) 125 | ret0, _ := ret[0].(error) 126 | return ret0 127 | } 128 | 129 | // DeleteCollection indicates an expected call of DeleteCollection 130 | func (mr *MockHorizontalPodAutoscalerInterfaceMockRecorder) DeleteCollection(arg0, arg1 interface{}) *gomock.Call { 131 | mr.mock.ctrl.T.Helper() 132 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCollection", reflect.TypeOf((*MockHorizontalPodAutoscalerInterface)(nil).DeleteCollection), arg0, arg1) 133 | } 134 | 135 | // Get mocks base method 136 | func (m *MockHorizontalPodAutoscalerInterface) Get(arg0 string, arg1 v10.GetOptions) (*v1.HorizontalPodAutoscaler, error) { 137 | m.ctrl.T.Helper() 138 | ret := m.ctrl.Call(m, "Get", arg0, arg1) 139 | ret0, _ := ret[0].(*v1.HorizontalPodAutoscaler) 140 | ret1, _ := ret[1].(error) 141 | return ret0, ret1 142 | } 143 | 144 | // Get indicates an expected call of Get 145 | func (mr *MockHorizontalPodAutoscalerInterfaceMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call { 146 | mr.mock.ctrl.T.Helper() 147 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockHorizontalPodAutoscalerInterface)(nil).Get), arg0, arg1) 148 | } 149 | 150 | // List mocks base method 151 | func (m *MockHorizontalPodAutoscalerInterface) List(arg0 v10.ListOptions) (*v1.HorizontalPodAutoscalerList, error) { 152 | m.ctrl.T.Helper() 153 | ret := m.ctrl.Call(m, "List", arg0) 154 | ret0, _ := ret[0].(*v1.HorizontalPodAutoscalerList) 155 | ret1, _ := ret[1].(error) 156 | return ret0, ret1 157 | } 158 | 159 | // List indicates an expected call of List 160 | func (mr *MockHorizontalPodAutoscalerInterfaceMockRecorder) List(arg0 interface{}) *gomock.Call { 161 | mr.mock.ctrl.T.Helper() 162 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockHorizontalPodAutoscalerInterface)(nil).List), arg0) 163 | } 164 | 165 | // Patch mocks base method 166 | func (m *MockHorizontalPodAutoscalerInterface) Patch(arg0 string, arg1 types.PatchType, arg2 []byte, arg3 ...string) (*v1.HorizontalPodAutoscaler, error) { 167 | m.ctrl.T.Helper() 168 | varargs := []interface{}{arg0, arg1, arg2} 169 | for _, a := range arg3 { 170 | varargs = append(varargs, a) 171 | } 172 | ret := m.ctrl.Call(m, "Patch", varargs...) 173 | ret0, _ := ret[0].(*v1.HorizontalPodAutoscaler) 174 | ret1, _ := ret[1].(error) 175 | return ret0, ret1 176 | } 177 | 178 | // Patch indicates an expected call of Patch 179 | func (mr *MockHorizontalPodAutoscalerInterfaceMockRecorder) Patch(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { 180 | mr.mock.ctrl.T.Helper() 181 | varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) 182 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockHorizontalPodAutoscalerInterface)(nil).Patch), varargs...) 183 | } 184 | 185 | // Update mocks base method 186 | func (m *MockHorizontalPodAutoscalerInterface) Update(arg0 *v1.HorizontalPodAutoscaler) (*v1.HorizontalPodAutoscaler, error) { 187 | m.ctrl.T.Helper() 188 | ret := m.ctrl.Call(m, "Update", arg0) 189 | ret0, _ := ret[0].(*v1.HorizontalPodAutoscaler) 190 | ret1, _ := ret[1].(error) 191 | return ret0, ret1 192 | } 193 | 194 | // Update indicates an expected call of Update 195 | func (mr *MockHorizontalPodAutoscalerInterfaceMockRecorder) Update(arg0 interface{}) *gomock.Call { 196 | mr.mock.ctrl.T.Helper() 197 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockHorizontalPodAutoscalerInterface)(nil).Update), arg0) 198 | } 199 | 200 | // UpdateStatus mocks base method 201 | func (m *MockHorizontalPodAutoscalerInterface) UpdateStatus(arg0 *v1.HorizontalPodAutoscaler) (*v1.HorizontalPodAutoscaler, error) { 202 | m.ctrl.T.Helper() 203 | ret := m.ctrl.Call(m, "UpdateStatus", arg0) 204 | ret0, _ := ret[0].(*v1.HorizontalPodAutoscaler) 205 | ret1, _ := ret[1].(error) 206 | return ret0, ret1 207 | } 208 | 209 | // UpdateStatus indicates an expected call of UpdateStatus 210 | func (mr *MockHorizontalPodAutoscalerInterfaceMockRecorder) UpdateStatus(arg0 interface{}) *gomock.Call { 211 | mr.mock.ctrl.T.Helper() 212 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStatus", reflect.TypeOf((*MockHorizontalPodAutoscalerInterface)(nil).UpdateStatus), arg0) 213 | } 214 | 215 | // Watch mocks base method 216 | func (m *MockHorizontalPodAutoscalerInterface) Watch(arg0 v10.ListOptions) (watch.Interface, error) { 217 | m.ctrl.T.Helper() 218 | ret := m.ctrl.Call(m, "Watch", arg0) 219 | ret0, _ := ret[0].(watch.Interface) 220 | ret1, _ := ret[1].(error) 221 | return ret0, ret1 222 | } 223 | 224 | // Watch indicates an expected call of Watch 225 | func (mr *MockHorizontalPodAutoscalerInterfaceMockRecorder) Watch(arg0 interface{}) *gomock.Call { 226 | mr.mock.ctrl.T.Helper() 227 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockHorizontalPodAutoscalerInterface)(nil).Watch), arg0) 228 | } 229 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /scaling-controller.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "reflect" 7 | "sync" 8 | "time" 9 | 10 | "go.uber.org/multierr" 11 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 12 | 13 | "github.com/golang/glog" 14 | "github.com/robfig/cron" 15 | 16 | apierr "k8s.io/apimachinery/pkg/api/errors" 17 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 | "k8s.io/client-go/kubernetes" 19 | "k8s.io/client-go/tools/cache" 20 | "k8s.io/client-go/tools/clientcmd" 21 | 22 | "context" 23 | "golang.org/x/oauth2/google" 24 | "google.golang.org/api/compute/v1" 25 | scalingv1alpha1 "k8s.restdev.com/operators/pkg/apis/scaling/v1alpha1" 26 | clientset "k8s.restdev.com/operators/pkg/client/clientset/versioned" 27 | informers "k8s.restdev.com/operators/pkg/client/informers/externalversions" 28 | listers "k8s.restdev.com/operators/pkg/client/listers/scaling/v1alpha1" 29 | scalingcron "k8s.restdev.com/operators/pkg/services/scaling/cron" 30 | scalingmetadata "k8s.restdev.com/operators/pkg/services/scaling/metadata" 31 | scalingstep "k8s.restdev.com/operators/pkg/services/scaling/step" 32 | ) 33 | 34 | /** 35 | * 36 | * Target Struct 37 | * 38 | */ 39 | type ScheduledScalerTarget struct { 40 | Name string 41 | Kind string 42 | Cron *cron.Cron 43 | } 44 | 45 | /** 46 | * 47 | * Scaling Controller Struct 48 | * 49 | */ 50 | type ScheduledScalerController struct { 51 | maxRetries int 52 | cronProxy scalingcron.CronProxy 53 | informerFactory informers.SharedInformerFactory 54 | restdevClient clientset.Interface 55 | kubeClient kubernetes.Interface 56 | scheduledScalersLister listers.ScheduledScalerLister 57 | scheduledScalersSynced cache.InformerSynced 58 | scheduledScalerTargets []ScheduledScalerTarget 59 | } 60 | 61 | /** 62 | * 63 | * Start the shared informers and wait for cache to sync 64 | * 65 | */ 66 | func (c *ScheduledScalerController) Run(stopCh chan struct{}) error { 67 | c.informerFactory.Start(stopCh) 68 | if !cache.WaitForCacheSync(stopCh, c.scheduledScalersSynced) { 69 | return fmt.Errorf("Failed to sync") 70 | } 71 | return nil 72 | } 73 | 74 | // validateScheduledScaler validates the scheduledScaler 75 | func (c *ScheduledScalerController) validateScheduledScaler(scheduledScaler *scalingv1alpha1.ScheduledScaler) error { 76 | if scheduledScaler == nil { 77 | return fmt.Errorf("scheduledScaler is nil") 78 | } 79 | var err error 80 | for _, step := range scheduledScaler.Spec.Steps { 81 | schedule, stepErr := c.cronProxy.Parse(step.Runat) 82 | if stepErr != nil { 83 | err = multierr.Append(err, fmt.Errorf("error parsing Runat %s: %w", step.Runat, stepErr)) 84 | continue 85 | } 86 | when := schedule.Next(time.Now()) 87 | if when.IsZero() { 88 | err = multierr.Append(err, fmt.Errorf("invalid Runat %s; will never fire", step.Runat)) 89 | } 90 | } 91 | return err 92 | } 93 | 94 | /** 95 | * 96 | * Add methods 97 | * 98 | * These methods will handle new resources 99 | * 100 | */ 101 | func (c *ScheduledScalerController) scheduledScalerAdd(obj interface{}) { 102 | scheduledScaler, ok := obj.(*scalingv1alpha1.ScheduledScaler) 103 | if !ok { 104 | utilruntime.HandleError(fmt.Errorf( 105 | "object %T is not a *scalingv1alpha1.ScheduledScaler; will not add", obj)) 106 | return 107 | } 108 | if err := c.validateScheduledScaler(scheduledScaler); err != nil { 109 | utilruntime.HandleError(fmt.Errorf( 110 | "error validating scheduledScaler %#v: %w; will not add", scheduledScaler, err)) 111 | return 112 | } 113 | if scheduledScaler.Spec.Target.Kind == "HorizontalPodAutoscaler" { 114 | c.scheduledScalerHpaCronAdd(scheduledScaler) 115 | } else if scheduledScaler.Spec.Target.Kind == "InstanceGroup" { 116 | c.scheduledScalerIgCronAdd(scheduledScaler) 117 | } 118 | } 119 | 120 | // scheduledScalerHpaCronAdd will update the hpa when the scheduled scaler fires. 121 | func (c *ScheduledScalerController) scheduledScalerHpaCronAdd(scheduledScaler *scalingv1alpha1.ScheduledScaler) { 122 | tz := scheduledScaler.Spec.TimeZone 123 | ss, err := c.scheduledScalersLister.ScheduledScalers(scheduledScaler.Namespace).Get(scheduledScaler.Name) 124 | if err != nil { 125 | utilruntime.HandleError(fmt.Errorf( 126 | "FAILED TO GET SCHEDULED SCALER: %s - %w", scheduledScaler.Spec.Target.Name, err)) 127 | return 128 | } 129 | hpaClient := c.kubeClient.AutoscalingV1().HorizontalPodAutoscalers(scheduledScaler.Namespace) 130 | hpa, err := hpaClient.Get(scheduledScaler.Spec.Target.Name, metav1.GetOptions{}) 131 | if err != nil { 132 | utilruntime.HandleError(fmt.Errorf( 133 | "FAILED TO GET HPA: %s - %w", scheduledScaler.Spec.Target.Name, err)) 134 | return 135 | } 136 | 137 | ssClient := c.restdevClient.ScalingV1alpha1().ScheduledScalers(scheduledScaler.Namespace) 138 | // TODO: is this really needed? 139 | ssCopy := ss.DeepCopy() 140 | stepsCron, err := c.cronProxy.Create(tz) 141 | if err != nil { 142 | utilruntime.HandleError(fmt.Errorf( 143 | "FAILED TO CREATE CRON: %s - %w", scheduledScaler.Spec.Target.Name, err)) 144 | return 145 | } 146 | var mutex sync.Mutex 147 | for key := range ssCopy.Spec.Steps { 148 | step := scheduledScaler.Spec.Steps[key] 149 | min, max := scalingstep.Parse(step) 150 | c.cronProxy.Push(stepsCron, step.Runat, func() { 151 | // If this scheduled scaler retries, don't let the "next" one get overwritten by its retry. 152 | mutex.Lock() 153 | defer mutex.Unlock() 154 | 155 | hpaRetries := 0 156 | HpaAgain: 157 | if hpaRetries > c.maxRetries { 158 | utilruntime.HandleError(fmt.Errorf( 159 | "FAILED TO UPDATE HPA: %s after %d retries", scheduledScaler.Spec.Target.Name, hpaRetries)) 160 | return 161 | } 162 | hpa, err = hpaClient.Get(scheduledScaler.Spec.Target.Name, metav1.GetOptions{}) 163 | if err != nil { 164 | utilruntime.HandleError(fmt.Errorf( 165 | "FAILED TO UPDATE HPA: %s - %w", scheduledScaler.Spec.Target.Name, err)) 166 | return 167 | } 168 | hpa.Spec.MinReplicas = min 169 | hpa.Spec.MaxReplicas = *max 170 | _, err = hpaClient.Update(hpa) 171 | if apierr.IsConflict(err) { 172 | glog.Infof("FAILED TO UPDATE HPA: %s - %v; retrying", scheduledScaler.Spec.Target.Name, err) 173 | hpaRetries++ 174 | goto HpaAgain 175 | } 176 | if err != nil { 177 | utilruntime.HandleError(fmt.Errorf( 178 | "FAILED TO UPDATE HPA: %s - %w", scheduledScaler.Spec.Target.Name, err)) 179 | return 180 | } 181 | ssRetries := 0 182 | SSAgain: 183 | if ssRetries > c.maxRetries { 184 | utilruntime.HandleError(fmt.Errorf( 185 | "FAILED TO UPDATE SS: %s after %d retries", scheduledScaler.Name, ssRetries)) 186 | return 187 | } 188 | ss, err := ssClient.Get(scheduledScaler.Name, metav1.GetOptions{}) 189 | if err != nil { 190 | utilruntime.HandleError(fmt.Errorf( 191 | "FAILED TO UPDATE SCHEDULED SCALER STATUS: %s - %w", scheduledScaler.Name, err)) 192 | return 193 | } 194 | ss.Status.Mode = step.Mode 195 | ss.Status.MinReplicas = *min 196 | ss.Status.MaxReplicas = *max 197 | _, err = ssClient.Update(ss) 198 | if apierr.IsConflict(err) { 199 | glog.Infof("FAILED TO UPDATE SCHEDULED SCALER STATUS: %s - %v; retrying", scheduledScaler.Name, err) 200 | ssRetries++ 201 | goto SSAgain 202 | } 203 | if err != nil { 204 | utilruntime.HandleError(fmt.Errorf( 205 | "FAILED TO UPDATE SCHEDULED SCALER STATUS: %s - %w", scheduledScaler.Name, err)) 206 | return 207 | } 208 | glog.Infof("SETTING RANGE SCALER: %s/%s -> %s - %d:%d", scheduledScaler.Namespace, scheduledScaler.Name, scheduledScaler.Spec.Target.Name, *min, *max) 209 | }) 210 | } 211 | c.cronProxy.Start(stepsCron) 212 | c.scheduledScalerTargets = append(c.scheduledScalerTargets, ScheduledScalerTarget{scheduledScaler.Spec.Target.Name, scheduledScaler.Spec.Target.Kind, stepsCron}) 213 | glog.Infof("SCHEDULED SCALER CREATED: %s -> %s", scheduledScaler.Name, scheduledScaler.Spec.Target.Name) 214 | } 215 | 216 | func (c *ScheduledScalerController) scheduledScalerIgCronAdd(scheduledScaler *scalingv1alpha1.ScheduledScaler) { 217 | projectId, zone, _ := scalingmetadata.GetClusterInfo() 218 | tz := scheduledScaler.Spec.TimeZone 219 | ss, err := c.scheduledScalersLister.ScheduledScalers(scheduledScaler.Namespace).Get(scheduledScaler.Name) 220 | if err != nil { 221 | utilruntime.HandleError(err) 222 | return 223 | } 224 | 225 | ctx := context.Background() 226 | client, err := google.DefaultClient(ctx, compute.ComputeScope) 227 | if err != nil { 228 | utilruntime.HandleError(err) 229 | return 230 | } 231 | computeService, err := compute.New(client) 232 | if err != nil { 233 | utilruntime.HandleError(err) 234 | return 235 | } 236 | 237 | autoscaler, err := computeService.Autoscalers.Get(projectId, zone, scheduledScaler.Spec.Target.Name).Do() 238 | if err != nil { 239 | utilruntime.HandleError(err) 240 | return 241 | } 242 | 243 | ssCopy := ss.DeepCopy() 244 | stepsCron, err := c.cronProxy.Create(tz) 245 | if err != nil { 246 | utilruntime.HandleError(err) 247 | return 248 | } 249 | for key := range scheduledScaler.Spec.Steps { 250 | step := scheduledScaler.Spec.Steps[key] 251 | min, max := scalingstep.Parse(step) 252 | c.cronProxy.Push(stepsCron, step.Runat, func() { 253 | autoscaler, err = computeService.Autoscalers.Get(projectId, zone, scheduledScaler.Spec.Target.Name).Do() 254 | autoscaler.AutoscalingPolicy.MaxNumReplicas = int64(*max) 255 | autoscaler.AutoscalingPolicy.MinNumReplicas = int64(*min) 256 | _, err := computeService.Autoscalers.Update(projectId, zone, autoscaler).Do() 257 | if err != nil { 258 | utilruntime.HandleError(fmt.Errorf( 259 | "FAILED TO UPDATE IG AUTOSCALER: %s - %w", scheduledScaler.Spec.Target.Name, err)) 260 | return 261 | } 262 | ssCopy.Status.Mode = step.Mode 263 | ssCopy.Status.MinReplicas = *min 264 | ssCopy.Status.MaxReplicas = *max 265 | if _, err = c.restdevClient.ScalingV1alpha1().ScheduledScalers(scheduledScaler.Namespace).Update(ssCopy); err != nil { 266 | utilruntime.HandleError(fmt.Errorf( 267 | "FAILED TO UPDATE SCHEDULED SCALER STATUS: %s - %w", scheduledScaler.Name, err)) 268 | } 269 | glog.Infof("SETTING RANGE IG SCALER: %s -> %s - %d/%d", scheduledScaler.Name, scheduledScaler.Spec.Target.Name, *min, *max) 270 | }) 271 | } 272 | 273 | c.cronProxy.Start(stepsCron) 274 | c.scheduledScalerTargets = append(c.scheduledScalerTargets, ScheduledScalerTarget{scheduledScaler.Spec.Target.Name, scheduledScaler.Spec.Target.Kind, stepsCron}) 275 | glog.Infof("SCHEDULED SCALER CREATED: %s -> %s", scheduledScaler.Name, scheduledScaler.Spec.Target.Name) 276 | } 277 | 278 | /** 279 | * 280 | * Update methods 281 | * 282 | * These methods will handle updates to existing resources 283 | * 284 | */ 285 | func (c *ScheduledScalerController) scheduledScalerUpdate(old, new interface{}) { 286 | oldScheduledScaler := old.(*scalingv1alpha1.ScheduledScaler) 287 | newScheduledScaler := new.(*scalingv1alpha1.ScheduledScaler) 288 | if reflect.DeepEqual(oldScheduledScaler.Spec, newScheduledScaler.Spec) { 289 | return 290 | } 291 | c.scheduledScalerDelete(old) 292 | c.scheduledScalerAdd(new) 293 | } 294 | 295 | /** 296 | * 297 | * Delete methods 298 | * 299 | * These methods will handle deletion of resources 300 | * 301 | */ 302 | func (c *ScheduledScalerController) scheduledScalerDelete(obj interface{}) { 303 | c.scheduledScalerCronDelete(obj) 304 | } 305 | 306 | func (c *ScheduledScalerController) scheduledScalerCronDelete(obj interface{}) { 307 | scheduledScaler := obj.(*scalingv1alpha1.ScheduledScaler) 308 | // find index 309 | key, err := c.scheduledScalerFindTargetKey(scheduledScaler.Spec.Target.Name) 310 | if err { 311 | glog.Infof("FAILED TO DELETE SCALER TARGET: %s -> %s (NotFound)", scheduledScaler.Name, scheduledScaler.Spec.Target.Name) 312 | return 313 | } 314 | glog.Infof("STOPPING CRONS FOR SCALER TARGET: %s -> %s", scheduledScaler.Name, scheduledScaler.Spec.Target.Name) 315 | c.cronProxy.Stop(c.scheduledScalerTargets[key].Cron) 316 | c.scheduledScalerTargets[key] = c.scheduledScalerTargets[0] 317 | c.scheduledScalerTargets = c.scheduledScalerTargets[1:] 318 | glog.Infof("SCHEDULED SCALER TARGET DELETED: %s -> %s", scheduledScaler.Name, scheduledScaler.Spec.Target.Name) 319 | } 320 | 321 | func (c *ScheduledScalerController) scheduledScalerFindTargetKey(name string) (int, bool) { 322 | for key := range c.scheduledScalerTargets { 323 | if c.scheduledScalerTargets[key].Name == name { 324 | return key, false 325 | } 326 | } 327 | 328 | return -1, true 329 | } 330 | 331 | /** 332 | * 333 | * Create new instance of the Scaling Controller 334 | * 335 | */ 336 | func NewScheduledScalerController( 337 | maxRetries int, 338 | cronProxy scalingcron.CronProxy, 339 | informerFactory informers.SharedInformerFactory, 340 | restdevClient clientset.Interface, 341 | kubeClient kubernetes.Interface, 342 | ) *ScheduledScalerController { 343 | scheduledScalerInformer := informerFactory.Scaling().V1alpha1().ScheduledScalers() 344 | scheduledScalersLister := scheduledScalerInformer.Lister() 345 | var scheduledScalerTargets []ScheduledScalerTarget 346 | 347 | c := &ScheduledScalerController{ 348 | maxRetries: maxRetries, 349 | cronProxy: cronProxy, 350 | informerFactory: informerFactory, 351 | restdevClient: restdevClient, 352 | kubeClient: kubeClient, 353 | scheduledScalersLister: scheduledScalersLister, 354 | scheduledScalersSynced: scheduledScalerInformer.Informer().HasSynced, 355 | scheduledScalerTargets: scheduledScalerTargets, 356 | } 357 | scheduledScalerInformer.Informer().AddEventHandler( 358 | cache.ResourceEventHandlerFuncs{ 359 | AddFunc: c.scheduledScalerAdd, 360 | UpdateFunc: c.scheduledScalerUpdate, 361 | DeleteFunc: c.scheduledScalerDelete, 362 | }, 363 | ) 364 | return c 365 | } 366 | 367 | /** 368 | * 369 | * Run the app 370 | * 371 | */ 372 | func main() { 373 | var ( 374 | kubeconfig string 375 | maxRetries int 376 | ) 377 | 378 | flag.Set("logtostderr", "true") 379 | flag.IntVar(&maxRetries, "max-retries", 1, "maximum retries before failing to update HPA or SS") 380 | flag.StringVar(&kubeconfig, "kubeconfig", "", "absolute path to the kubeconfig file") 381 | flag.Parse() 382 | 383 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 384 | if err != nil { 385 | panic(err.Error()) 386 | } 387 | 388 | //clientset, err := kubernetes.NewForConfig(config) 389 | kubeClient, err := kubernetes.NewForConfig(config) 390 | restdevClient, err := clientset.NewForConfig(config) 391 | if err != nil { 392 | glog.Fatal(err) 393 | } 394 | 395 | factory := informers.NewSharedInformerFactory(restdevClient, time.Hour*24) 396 | cronProxy := new(scalingcron.CronImpl) 397 | controller := NewScheduledScalerController(maxRetries, cronProxy, factory, restdevClient, kubeClient) 398 | stop := make(chan struct{}) 399 | defer close(stop) 400 | err = controller.Run(stop) 401 | if err != nil { 402 | glog.Fatal(err) 403 | } 404 | select {} 405 | } 406 | -------------------------------------------------------------------------------- /test/external/corev1.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: k8s.io/client-go/kubernetes/typed/core/v1 (interfaces: CoreV1Interface,ConfigMapInterface) 3 | 4 | // Package mock_external is a generated GoMock package. 5 | package mock_external 6 | 7 | import ( 8 | gomock "github.com/golang/mock/gomock" 9 | v1 "k8s.io/api/core/v1" 10 | v10 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | types "k8s.io/apimachinery/pkg/types" 12 | watch "k8s.io/apimachinery/pkg/watch" 13 | v11 "k8s.io/client-go/kubernetes/typed/core/v1" 14 | rest "k8s.io/client-go/rest" 15 | reflect "reflect" 16 | ) 17 | 18 | // MockCoreV1Interface is a mock of CoreV1Interface interface 19 | type MockCoreV1Interface struct { 20 | ctrl *gomock.Controller 21 | recorder *MockCoreV1InterfaceMockRecorder 22 | } 23 | 24 | // MockCoreV1InterfaceMockRecorder is the mock recorder for MockCoreV1Interface 25 | type MockCoreV1InterfaceMockRecorder struct { 26 | mock *MockCoreV1Interface 27 | } 28 | 29 | // NewMockCoreV1Interface creates a new mock instance 30 | func NewMockCoreV1Interface(ctrl *gomock.Controller) *MockCoreV1Interface { 31 | mock := &MockCoreV1Interface{ctrl: ctrl} 32 | mock.recorder = &MockCoreV1InterfaceMockRecorder{mock} 33 | return mock 34 | } 35 | 36 | // EXPECT returns an object that allows the caller to indicate expected use 37 | func (m *MockCoreV1Interface) EXPECT() *MockCoreV1InterfaceMockRecorder { 38 | return m.recorder 39 | } 40 | 41 | // ComponentStatuses mocks base method 42 | func (m *MockCoreV1Interface) ComponentStatuses() v11.ComponentStatusInterface { 43 | m.ctrl.T.Helper() 44 | ret := m.ctrl.Call(m, "ComponentStatuses") 45 | ret0, _ := ret[0].(v11.ComponentStatusInterface) 46 | return ret0 47 | } 48 | 49 | // ComponentStatuses indicates an expected call of ComponentStatuses 50 | func (mr *MockCoreV1InterfaceMockRecorder) ComponentStatuses() *gomock.Call { 51 | mr.mock.ctrl.T.Helper() 52 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ComponentStatuses", reflect.TypeOf((*MockCoreV1Interface)(nil).ComponentStatuses)) 53 | } 54 | 55 | // ConfigMaps mocks base method 56 | func (m *MockCoreV1Interface) ConfigMaps(arg0 string) v11.ConfigMapInterface { 57 | m.ctrl.T.Helper() 58 | ret := m.ctrl.Call(m, "ConfigMaps", arg0) 59 | ret0, _ := ret[0].(v11.ConfigMapInterface) 60 | return ret0 61 | } 62 | 63 | // ConfigMaps indicates an expected call of ConfigMaps 64 | func (mr *MockCoreV1InterfaceMockRecorder) ConfigMaps(arg0 interface{}) *gomock.Call { 65 | mr.mock.ctrl.T.Helper() 66 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigMaps", reflect.TypeOf((*MockCoreV1Interface)(nil).ConfigMaps), arg0) 67 | } 68 | 69 | // Endpoints mocks base method 70 | func (m *MockCoreV1Interface) Endpoints(arg0 string) v11.EndpointsInterface { 71 | m.ctrl.T.Helper() 72 | ret := m.ctrl.Call(m, "Endpoints", arg0) 73 | ret0, _ := ret[0].(v11.EndpointsInterface) 74 | return ret0 75 | } 76 | 77 | // Endpoints indicates an expected call of Endpoints 78 | func (mr *MockCoreV1InterfaceMockRecorder) Endpoints(arg0 interface{}) *gomock.Call { 79 | mr.mock.ctrl.T.Helper() 80 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Endpoints", reflect.TypeOf((*MockCoreV1Interface)(nil).Endpoints), arg0) 81 | } 82 | 83 | // Events mocks base method 84 | func (m *MockCoreV1Interface) Events(arg0 string) v11.EventInterface { 85 | m.ctrl.T.Helper() 86 | ret := m.ctrl.Call(m, "Events", arg0) 87 | ret0, _ := ret[0].(v11.EventInterface) 88 | return ret0 89 | } 90 | 91 | // Events indicates an expected call of Events 92 | func (mr *MockCoreV1InterfaceMockRecorder) Events(arg0 interface{}) *gomock.Call { 93 | mr.mock.ctrl.T.Helper() 94 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Events", reflect.TypeOf((*MockCoreV1Interface)(nil).Events), arg0) 95 | } 96 | 97 | // LimitRanges mocks base method 98 | func (m *MockCoreV1Interface) LimitRanges(arg0 string) v11.LimitRangeInterface { 99 | m.ctrl.T.Helper() 100 | ret := m.ctrl.Call(m, "LimitRanges", arg0) 101 | ret0, _ := ret[0].(v11.LimitRangeInterface) 102 | return ret0 103 | } 104 | 105 | // LimitRanges indicates an expected call of LimitRanges 106 | func (mr *MockCoreV1InterfaceMockRecorder) LimitRanges(arg0 interface{}) *gomock.Call { 107 | mr.mock.ctrl.T.Helper() 108 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LimitRanges", reflect.TypeOf((*MockCoreV1Interface)(nil).LimitRanges), arg0) 109 | } 110 | 111 | // Namespaces mocks base method 112 | func (m *MockCoreV1Interface) Namespaces() v11.NamespaceInterface { 113 | m.ctrl.T.Helper() 114 | ret := m.ctrl.Call(m, "Namespaces") 115 | ret0, _ := ret[0].(v11.NamespaceInterface) 116 | return ret0 117 | } 118 | 119 | // Namespaces indicates an expected call of Namespaces 120 | func (mr *MockCoreV1InterfaceMockRecorder) Namespaces() *gomock.Call { 121 | mr.mock.ctrl.T.Helper() 122 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Namespaces", reflect.TypeOf((*MockCoreV1Interface)(nil).Namespaces)) 123 | } 124 | 125 | // Nodes mocks base method 126 | func (m *MockCoreV1Interface) Nodes() v11.NodeInterface { 127 | m.ctrl.T.Helper() 128 | ret := m.ctrl.Call(m, "Nodes") 129 | ret0, _ := ret[0].(v11.NodeInterface) 130 | return ret0 131 | } 132 | 133 | // Nodes indicates an expected call of Nodes 134 | func (mr *MockCoreV1InterfaceMockRecorder) Nodes() *gomock.Call { 135 | mr.mock.ctrl.T.Helper() 136 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Nodes", reflect.TypeOf((*MockCoreV1Interface)(nil).Nodes)) 137 | } 138 | 139 | // PersistentVolumeClaims mocks base method 140 | func (m *MockCoreV1Interface) PersistentVolumeClaims(arg0 string) v11.PersistentVolumeClaimInterface { 141 | m.ctrl.T.Helper() 142 | ret := m.ctrl.Call(m, "PersistentVolumeClaims", arg0) 143 | ret0, _ := ret[0].(v11.PersistentVolumeClaimInterface) 144 | return ret0 145 | } 146 | 147 | // PersistentVolumeClaims indicates an expected call of PersistentVolumeClaims 148 | func (mr *MockCoreV1InterfaceMockRecorder) PersistentVolumeClaims(arg0 interface{}) *gomock.Call { 149 | mr.mock.ctrl.T.Helper() 150 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PersistentVolumeClaims", reflect.TypeOf((*MockCoreV1Interface)(nil).PersistentVolumeClaims), arg0) 151 | } 152 | 153 | // PersistentVolumes mocks base method 154 | func (m *MockCoreV1Interface) PersistentVolumes() v11.PersistentVolumeInterface { 155 | m.ctrl.T.Helper() 156 | ret := m.ctrl.Call(m, "PersistentVolumes") 157 | ret0, _ := ret[0].(v11.PersistentVolumeInterface) 158 | return ret0 159 | } 160 | 161 | // PersistentVolumes indicates an expected call of PersistentVolumes 162 | func (mr *MockCoreV1InterfaceMockRecorder) PersistentVolumes() *gomock.Call { 163 | mr.mock.ctrl.T.Helper() 164 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PersistentVolumes", reflect.TypeOf((*MockCoreV1Interface)(nil).PersistentVolumes)) 165 | } 166 | 167 | // PodTemplates mocks base method 168 | func (m *MockCoreV1Interface) PodTemplates(arg0 string) v11.PodTemplateInterface { 169 | m.ctrl.T.Helper() 170 | ret := m.ctrl.Call(m, "PodTemplates", arg0) 171 | ret0, _ := ret[0].(v11.PodTemplateInterface) 172 | return ret0 173 | } 174 | 175 | // PodTemplates indicates an expected call of PodTemplates 176 | func (mr *MockCoreV1InterfaceMockRecorder) PodTemplates(arg0 interface{}) *gomock.Call { 177 | mr.mock.ctrl.T.Helper() 178 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PodTemplates", reflect.TypeOf((*MockCoreV1Interface)(nil).PodTemplates), arg0) 179 | } 180 | 181 | // Pods mocks base method 182 | func (m *MockCoreV1Interface) Pods(arg0 string) v11.PodInterface { 183 | m.ctrl.T.Helper() 184 | ret := m.ctrl.Call(m, "Pods", arg0) 185 | ret0, _ := ret[0].(v11.PodInterface) 186 | return ret0 187 | } 188 | 189 | // Pods indicates an expected call of Pods 190 | func (mr *MockCoreV1InterfaceMockRecorder) Pods(arg0 interface{}) *gomock.Call { 191 | mr.mock.ctrl.T.Helper() 192 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pods", reflect.TypeOf((*MockCoreV1Interface)(nil).Pods), arg0) 193 | } 194 | 195 | // RESTClient mocks base method 196 | func (m *MockCoreV1Interface) RESTClient() rest.Interface { 197 | m.ctrl.T.Helper() 198 | ret := m.ctrl.Call(m, "RESTClient") 199 | ret0, _ := ret[0].(rest.Interface) 200 | return ret0 201 | } 202 | 203 | // RESTClient indicates an expected call of RESTClient 204 | func (mr *MockCoreV1InterfaceMockRecorder) RESTClient() *gomock.Call { 205 | mr.mock.ctrl.T.Helper() 206 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RESTClient", reflect.TypeOf((*MockCoreV1Interface)(nil).RESTClient)) 207 | } 208 | 209 | // ReplicationControllers mocks base method 210 | func (m *MockCoreV1Interface) ReplicationControllers(arg0 string) v11.ReplicationControllerInterface { 211 | m.ctrl.T.Helper() 212 | ret := m.ctrl.Call(m, "ReplicationControllers", arg0) 213 | ret0, _ := ret[0].(v11.ReplicationControllerInterface) 214 | return ret0 215 | } 216 | 217 | // ReplicationControllers indicates an expected call of ReplicationControllers 218 | func (mr *MockCoreV1InterfaceMockRecorder) ReplicationControllers(arg0 interface{}) *gomock.Call { 219 | mr.mock.ctrl.T.Helper() 220 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicationControllers", reflect.TypeOf((*MockCoreV1Interface)(nil).ReplicationControllers), arg0) 221 | } 222 | 223 | // ResourceQuotas mocks base method 224 | func (m *MockCoreV1Interface) ResourceQuotas(arg0 string) v11.ResourceQuotaInterface { 225 | m.ctrl.T.Helper() 226 | ret := m.ctrl.Call(m, "ResourceQuotas", arg0) 227 | ret0, _ := ret[0].(v11.ResourceQuotaInterface) 228 | return ret0 229 | } 230 | 231 | // ResourceQuotas indicates an expected call of ResourceQuotas 232 | func (mr *MockCoreV1InterfaceMockRecorder) ResourceQuotas(arg0 interface{}) *gomock.Call { 233 | mr.mock.ctrl.T.Helper() 234 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResourceQuotas", reflect.TypeOf((*MockCoreV1Interface)(nil).ResourceQuotas), arg0) 235 | } 236 | 237 | // Secrets mocks base method 238 | func (m *MockCoreV1Interface) Secrets(arg0 string) v11.SecretInterface { 239 | m.ctrl.T.Helper() 240 | ret := m.ctrl.Call(m, "Secrets", arg0) 241 | ret0, _ := ret[0].(v11.SecretInterface) 242 | return ret0 243 | } 244 | 245 | // Secrets indicates an expected call of Secrets 246 | func (mr *MockCoreV1InterfaceMockRecorder) Secrets(arg0 interface{}) *gomock.Call { 247 | mr.mock.ctrl.T.Helper() 248 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Secrets", reflect.TypeOf((*MockCoreV1Interface)(nil).Secrets), arg0) 249 | } 250 | 251 | // ServiceAccounts mocks base method 252 | func (m *MockCoreV1Interface) ServiceAccounts(arg0 string) v11.ServiceAccountInterface { 253 | m.ctrl.T.Helper() 254 | ret := m.ctrl.Call(m, "ServiceAccounts", arg0) 255 | ret0, _ := ret[0].(v11.ServiceAccountInterface) 256 | return ret0 257 | } 258 | 259 | // ServiceAccounts indicates an expected call of ServiceAccounts 260 | func (mr *MockCoreV1InterfaceMockRecorder) ServiceAccounts(arg0 interface{}) *gomock.Call { 261 | mr.mock.ctrl.T.Helper() 262 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceAccounts", reflect.TypeOf((*MockCoreV1Interface)(nil).ServiceAccounts), arg0) 263 | } 264 | 265 | // Services mocks base method 266 | func (m *MockCoreV1Interface) Services(arg0 string) v11.ServiceInterface { 267 | m.ctrl.T.Helper() 268 | ret := m.ctrl.Call(m, "Services", arg0) 269 | ret0, _ := ret[0].(v11.ServiceInterface) 270 | return ret0 271 | } 272 | 273 | // Services indicates an expected call of Services 274 | func (mr *MockCoreV1InterfaceMockRecorder) Services(arg0 interface{}) *gomock.Call { 275 | mr.mock.ctrl.T.Helper() 276 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Services", reflect.TypeOf((*MockCoreV1Interface)(nil).Services), arg0) 277 | } 278 | 279 | // MockConfigMapInterface is a mock of ConfigMapInterface interface 280 | type MockConfigMapInterface struct { 281 | ctrl *gomock.Controller 282 | recorder *MockConfigMapInterfaceMockRecorder 283 | } 284 | 285 | // MockConfigMapInterfaceMockRecorder is the mock recorder for MockConfigMapInterface 286 | type MockConfigMapInterfaceMockRecorder struct { 287 | mock *MockConfigMapInterface 288 | } 289 | 290 | // NewMockConfigMapInterface creates a new mock instance 291 | func NewMockConfigMapInterface(ctrl *gomock.Controller) *MockConfigMapInterface { 292 | mock := &MockConfigMapInterface{ctrl: ctrl} 293 | mock.recorder = &MockConfigMapInterfaceMockRecorder{mock} 294 | return mock 295 | } 296 | 297 | // EXPECT returns an object that allows the caller to indicate expected use 298 | func (m *MockConfigMapInterface) EXPECT() *MockConfigMapInterfaceMockRecorder { 299 | return m.recorder 300 | } 301 | 302 | // Create mocks base method 303 | func (m *MockConfigMapInterface) Create(arg0 *v1.ConfigMap) (*v1.ConfigMap, error) { 304 | m.ctrl.T.Helper() 305 | ret := m.ctrl.Call(m, "Create", arg0) 306 | ret0, _ := ret[0].(*v1.ConfigMap) 307 | ret1, _ := ret[1].(error) 308 | return ret0, ret1 309 | } 310 | 311 | // Create indicates an expected call of Create 312 | func (mr *MockConfigMapInterfaceMockRecorder) Create(arg0 interface{}) *gomock.Call { 313 | mr.mock.ctrl.T.Helper() 314 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockConfigMapInterface)(nil).Create), arg0) 315 | } 316 | 317 | // Delete mocks base method 318 | func (m *MockConfigMapInterface) Delete(arg0 string, arg1 *v10.DeleteOptions) error { 319 | m.ctrl.T.Helper() 320 | ret := m.ctrl.Call(m, "Delete", arg0, arg1) 321 | ret0, _ := ret[0].(error) 322 | return ret0 323 | } 324 | 325 | // Delete indicates an expected call of Delete 326 | func (mr *MockConfigMapInterfaceMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { 327 | mr.mock.ctrl.T.Helper() 328 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockConfigMapInterface)(nil).Delete), arg0, arg1) 329 | } 330 | 331 | // DeleteCollection mocks base method 332 | func (m *MockConfigMapInterface) DeleteCollection(arg0 *v10.DeleteOptions, arg1 v10.ListOptions) error { 333 | m.ctrl.T.Helper() 334 | ret := m.ctrl.Call(m, "DeleteCollection", arg0, arg1) 335 | ret0, _ := ret[0].(error) 336 | return ret0 337 | } 338 | 339 | // DeleteCollection indicates an expected call of DeleteCollection 340 | func (mr *MockConfigMapInterfaceMockRecorder) DeleteCollection(arg0, arg1 interface{}) *gomock.Call { 341 | mr.mock.ctrl.T.Helper() 342 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCollection", reflect.TypeOf((*MockConfigMapInterface)(nil).DeleteCollection), arg0, arg1) 343 | } 344 | 345 | // Get mocks base method 346 | func (m *MockConfigMapInterface) Get(arg0 string, arg1 v10.GetOptions) (*v1.ConfigMap, error) { 347 | m.ctrl.T.Helper() 348 | ret := m.ctrl.Call(m, "Get", arg0, arg1) 349 | ret0, _ := ret[0].(*v1.ConfigMap) 350 | ret1, _ := ret[1].(error) 351 | return ret0, ret1 352 | } 353 | 354 | // Get indicates an expected call of Get 355 | func (mr *MockConfigMapInterfaceMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call { 356 | mr.mock.ctrl.T.Helper() 357 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockConfigMapInterface)(nil).Get), arg0, arg1) 358 | } 359 | 360 | // List mocks base method 361 | func (m *MockConfigMapInterface) List(arg0 v10.ListOptions) (*v1.ConfigMapList, error) { 362 | m.ctrl.T.Helper() 363 | ret := m.ctrl.Call(m, "List", arg0) 364 | ret0, _ := ret[0].(*v1.ConfigMapList) 365 | ret1, _ := ret[1].(error) 366 | return ret0, ret1 367 | } 368 | 369 | // List indicates an expected call of List 370 | func (mr *MockConfigMapInterfaceMockRecorder) List(arg0 interface{}) *gomock.Call { 371 | mr.mock.ctrl.T.Helper() 372 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockConfigMapInterface)(nil).List), arg0) 373 | } 374 | 375 | // Patch mocks base method 376 | func (m *MockConfigMapInterface) Patch(arg0 string, arg1 types.PatchType, arg2 []byte, arg3 ...string) (*v1.ConfigMap, error) { 377 | m.ctrl.T.Helper() 378 | varargs := []interface{}{arg0, arg1, arg2} 379 | for _, a := range arg3 { 380 | varargs = append(varargs, a) 381 | } 382 | ret := m.ctrl.Call(m, "Patch", varargs...) 383 | ret0, _ := ret[0].(*v1.ConfigMap) 384 | ret1, _ := ret[1].(error) 385 | return ret0, ret1 386 | } 387 | 388 | // Patch indicates an expected call of Patch 389 | func (mr *MockConfigMapInterfaceMockRecorder) Patch(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { 390 | mr.mock.ctrl.T.Helper() 391 | varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) 392 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockConfigMapInterface)(nil).Patch), varargs...) 393 | } 394 | 395 | // Update mocks base method 396 | func (m *MockConfigMapInterface) Update(arg0 *v1.ConfigMap) (*v1.ConfigMap, error) { 397 | m.ctrl.T.Helper() 398 | ret := m.ctrl.Call(m, "Update", arg0) 399 | ret0, _ := ret[0].(*v1.ConfigMap) 400 | ret1, _ := ret[1].(error) 401 | return ret0, ret1 402 | } 403 | 404 | // Update indicates an expected call of Update 405 | func (mr *MockConfigMapInterfaceMockRecorder) Update(arg0 interface{}) *gomock.Call { 406 | mr.mock.ctrl.T.Helper() 407 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockConfigMapInterface)(nil).Update), arg0) 408 | } 409 | 410 | // Watch mocks base method 411 | func (m *MockConfigMapInterface) Watch(arg0 v10.ListOptions) (watch.Interface, error) { 412 | m.ctrl.T.Helper() 413 | ret := m.ctrl.Call(m, "Watch", arg0) 414 | ret0, _ := ret[0].(watch.Interface) 415 | ret1, _ := ret[1].(error) 416 | return ret0, ret1 417 | } 418 | 419 | // Watch indicates an expected call of Watch 420 | func (mr *MockConfigMapInterfaceMockRecorder) Watch(arg0 interface{}) *gomock.Call { 421 | mr.mock.ctrl.T.Helper() 422 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockConfigMapInterface)(nil).Watch), arg0) 423 | } 424 | -------------------------------------------------------------------------------- /test/external/kubernetes.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: k8s.io/client-go/kubernetes (interfaces: Interface) 3 | 4 | // Package mock_external is a generated GoMock package. 5 | package mock_external 6 | 7 | import ( 8 | gomock "github.com/golang/mock/gomock" 9 | discovery "k8s.io/client-go/discovery" 10 | v1alpha1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1" 11 | v1beta1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1" 12 | v1 "k8s.io/client-go/kubernetes/typed/apps/v1" 13 | v1beta10 "k8s.io/client-go/kubernetes/typed/apps/v1beta1" 14 | v1beta2 "k8s.io/client-go/kubernetes/typed/apps/v1beta2" 15 | v10 "k8s.io/client-go/kubernetes/typed/authentication/v1" 16 | v1beta11 "k8s.io/client-go/kubernetes/typed/authentication/v1beta1" 17 | v11 "k8s.io/client-go/kubernetes/typed/authorization/v1" 18 | v1beta12 "k8s.io/client-go/kubernetes/typed/authorization/v1beta1" 19 | v12 "k8s.io/client-go/kubernetes/typed/autoscaling/v1" 20 | v2beta1 "k8s.io/client-go/kubernetes/typed/autoscaling/v2beta1" 21 | v13 "k8s.io/client-go/kubernetes/typed/batch/v1" 22 | v1beta13 "k8s.io/client-go/kubernetes/typed/batch/v1beta1" 23 | v2alpha1 "k8s.io/client-go/kubernetes/typed/batch/v2alpha1" 24 | v1beta14 "k8s.io/client-go/kubernetes/typed/certificates/v1beta1" 25 | v14 "k8s.io/client-go/kubernetes/typed/core/v1" 26 | v1beta15 "k8s.io/client-go/kubernetes/typed/events/v1beta1" 27 | v1beta16 "k8s.io/client-go/kubernetes/typed/extensions/v1beta1" 28 | v15 "k8s.io/client-go/kubernetes/typed/networking/v1" 29 | v1beta17 "k8s.io/client-go/kubernetes/typed/policy/v1beta1" 30 | v16 "k8s.io/client-go/kubernetes/typed/rbac/v1" 31 | v1alpha10 "k8s.io/client-go/kubernetes/typed/rbac/v1alpha1" 32 | v1beta18 "k8s.io/client-go/kubernetes/typed/rbac/v1beta1" 33 | v1alpha11 "k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1" 34 | v1alpha12 "k8s.io/client-go/kubernetes/typed/settings/v1alpha1" 35 | v17 "k8s.io/client-go/kubernetes/typed/storage/v1" 36 | v1alpha13 "k8s.io/client-go/kubernetes/typed/storage/v1alpha1" 37 | v1beta19 "k8s.io/client-go/kubernetes/typed/storage/v1beta1" 38 | reflect "reflect" 39 | ) 40 | 41 | // MockInterface is a mock of Interface interface 42 | type MockInterface struct { 43 | ctrl *gomock.Controller 44 | recorder *MockInterfaceMockRecorder 45 | } 46 | 47 | // MockInterfaceMockRecorder is the mock recorder for MockInterface 48 | type MockInterfaceMockRecorder struct { 49 | mock *MockInterface 50 | } 51 | 52 | // NewMockInterface creates a new mock instance 53 | func NewMockInterface(ctrl *gomock.Controller) *MockInterface { 54 | mock := &MockInterface{ctrl: ctrl} 55 | mock.recorder = &MockInterfaceMockRecorder{mock} 56 | return mock 57 | } 58 | 59 | // EXPECT returns an object that allows the caller to indicate expected use 60 | func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder { 61 | return m.recorder 62 | } 63 | 64 | // Admissionregistration mocks base method 65 | func (m *MockInterface) Admissionregistration() v1beta1.AdmissionregistrationV1beta1Interface { 66 | m.ctrl.T.Helper() 67 | ret := m.ctrl.Call(m, "Admissionregistration") 68 | ret0, _ := ret[0].(v1beta1.AdmissionregistrationV1beta1Interface) 69 | return ret0 70 | } 71 | 72 | // Admissionregistration indicates an expected call of Admissionregistration 73 | func (mr *MockInterfaceMockRecorder) Admissionregistration() *gomock.Call { 74 | mr.mock.ctrl.T.Helper() 75 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Admissionregistration", reflect.TypeOf((*MockInterface)(nil).Admissionregistration)) 76 | } 77 | 78 | // AdmissionregistrationV1alpha1 mocks base method 79 | func (m *MockInterface) AdmissionregistrationV1alpha1() v1alpha1.AdmissionregistrationV1alpha1Interface { 80 | m.ctrl.T.Helper() 81 | ret := m.ctrl.Call(m, "AdmissionregistrationV1alpha1") 82 | ret0, _ := ret[0].(v1alpha1.AdmissionregistrationV1alpha1Interface) 83 | return ret0 84 | } 85 | 86 | // AdmissionregistrationV1alpha1 indicates an expected call of AdmissionregistrationV1alpha1 87 | func (mr *MockInterfaceMockRecorder) AdmissionregistrationV1alpha1() *gomock.Call { 88 | mr.mock.ctrl.T.Helper() 89 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdmissionregistrationV1alpha1", reflect.TypeOf((*MockInterface)(nil).AdmissionregistrationV1alpha1)) 90 | } 91 | 92 | // AdmissionregistrationV1beta1 mocks base method 93 | func (m *MockInterface) AdmissionregistrationV1beta1() v1beta1.AdmissionregistrationV1beta1Interface { 94 | m.ctrl.T.Helper() 95 | ret := m.ctrl.Call(m, "AdmissionregistrationV1beta1") 96 | ret0, _ := ret[0].(v1beta1.AdmissionregistrationV1beta1Interface) 97 | return ret0 98 | } 99 | 100 | // AdmissionregistrationV1beta1 indicates an expected call of AdmissionregistrationV1beta1 101 | func (mr *MockInterfaceMockRecorder) AdmissionregistrationV1beta1() *gomock.Call { 102 | mr.mock.ctrl.T.Helper() 103 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdmissionregistrationV1beta1", reflect.TypeOf((*MockInterface)(nil).AdmissionregistrationV1beta1)) 104 | } 105 | 106 | // Apps mocks base method 107 | func (m *MockInterface) Apps() v1.AppsV1Interface { 108 | m.ctrl.T.Helper() 109 | ret := m.ctrl.Call(m, "Apps") 110 | ret0, _ := ret[0].(v1.AppsV1Interface) 111 | return ret0 112 | } 113 | 114 | // Apps indicates an expected call of Apps 115 | func (mr *MockInterfaceMockRecorder) Apps() *gomock.Call { 116 | mr.mock.ctrl.T.Helper() 117 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Apps", reflect.TypeOf((*MockInterface)(nil).Apps)) 118 | } 119 | 120 | // AppsV1 mocks base method 121 | func (m *MockInterface) AppsV1() v1.AppsV1Interface { 122 | m.ctrl.T.Helper() 123 | ret := m.ctrl.Call(m, "AppsV1") 124 | ret0, _ := ret[0].(v1.AppsV1Interface) 125 | return ret0 126 | } 127 | 128 | // AppsV1 indicates an expected call of AppsV1 129 | func (mr *MockInterfaceMockRecorder) AppsV1() *gomock.Call { 130 | mr.mock.ctrl.T.Helper() 131 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppsV1", reflect.TypeOf((*MockInterface)(nil).AppsV1)) 132 | } 133 | 134 | // AppsV1beta1 mocks base method 135 | func (m *MockInterface) AppsV1beta1() v1beta10.AppsV1beta1Interface { 136 | m.ctrl.T.Helper() 137 | ret := m.ctrl.Call(m, "AppsV1beta1") 138 | ret0, _ := ret[0].(v1beta10.AppsV1beta1Interface) 139 | return ret0 140 | } 141 | 142 | // AppsV1beta1 indicates an expected call of AppsV1beta1 143 | func (mr *MockInterfaceMockRecorder) AppsV1beta1() *gomock.Call { 144 | mr.mock.ctrl.T.Helper() 145 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppsV1beta1", reflect.TypeOf((*MockInterface)(nil).AppsV1beta1)) 146 | } 147 | 148 | // AppsV1beta2 mocks base method 149 | func (m *MockInterface) AppsV1beta2() v1beta2.AppsV1beta2Interface { 150 | m.ctrl.T.Helper() 151 | ret := m.ctrl.Call(m, "AppsV1beta2") 152 | ret0, _ := ret[0].(v1beta2.AppsV1beta2Interface) 153 | return ret0 154 | } 155 | 156 | // AppsV1beta2 indicates an expected call of AppsV1beta2 157 | func (mr *MockInterfaceMockRecorder) AppsV1beta2() *gomock.Call { 158 | mr.mock.ctrl.T.Helper() 159 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppsV1beta2", reflect.TypeOf((*MockInterface)(nil).AppsV1beta2)) 160 | } 161 | 162 | // Authentication mocks base method 163 | func (m *MockInterface) Authentication() v10.AuthenticationV1Interface { 164 | m.ctrl.T.Helper() 165 | ret := m.ctrl.Call(m, "Authentication") 166 | ret0, _ := ret[0].(v10.AuthenticationV1Interface) 167 | return ret0 168 | } 169 | 170 | // Authentication indicates an expected call of Authentication 171 | func (mr *MockInterfaceMockRecorder) Authentication() *gomock.Call { 172 | mr.mock.ctrl.T.Helper() 173 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Authentication", reflect.TypeOf((*MockInterface)(nil).Authentication)) 174 | } 175 | 176 | // AuthenticationV1 mocks base method 177 | func (m *MockInterface) AuthenticationV1() v10.AuthenticationV1Interface { 178 | m.ctrl.T.Helper() 179 | ret := m.ctrl.Call(m, "AuthenticationV1") 180 | ret0, _ := ret[0].(v10.AuthenticationV1Interface) 181 | return ret0 182 | } 183 | 184 | // AuthenticationV1 indicates an expected call of AuthenticationV1 185 | func (mr *MockInterfaceMockRecorder) AuthenticationV1() *gomock.Call { 186 | mr.mock.ctrl.T.Helper() 187 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticationV1", reflect.TypeOf((*MockInterface)(nil).AuthenticationV1)) 188 | } 189 | 190 | // AuthenticationV1beta1 mocks base method 191 | func (m *MockInterface) AuthenticationV1beta1() v1beta11.AuthenticationV1beta1Interface { 192 | m.ctrl.T.Helper() 193 | ret := m.ctrl.Call(m, "AuthenticationV1beta1") 194 | ret0, _ := ret[0].(v1beta11.AuthenticationV1beta1Interface) 195 | return ret0 196 | } 197 | 198 | // AuthenticationV1beta1 indicates an expected call of AuthenticationV1beta1 199 | func (mr *MockInterfaceMockRecorder) AuthenticationV1beta1() *gomock.Call { 200 | mr.mock.ctrl.T.Helper() 201 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticationV1beta1", reflect.TypeOf((*MockInterface)(nil).AuthenticationV1beta1)) 202 | } 203 | 204 | // Authorization mocks base method 205 | func (m *MockInterface) Authorization() v11.AuthorizationV1Interface { 206 | m.ctrl.T.Helper() 207 | ret := m.ctrl.Call(m, "Authorization") 208 | ret0, _ := ret[0].(v11.AuthorizationV1Interface) 209 | return ret0 210 | } 211 | 212 | // Authorization indicates an expected call of Authorization 213 | func (mr *MockInterfaceMockRecorder) Authorization() *gomock.Call { 214 | mr.mock.ctrl.T.Helper() 215 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Authorization", reflect.TypeOf((*MockInterface)(nil).Authorization)) 216 | } 217 | 218 | // AuthorizationV1 mocks base method 219 | func (m *MockInterface) AuthorizationV1() v11.AuthorizationV1Interface { 220 | m.ctrl.T.Helper() 221 | ret := m.ctrl.Call(m, "AuthorizationV1") 222 | ret0, _ := ret[0].(v11.AuthorizationV1Interface) 223 | return ret0 224 | } 225 | 226 | // AuthorizationV1 indicates an expected call of AuthorizationV1 227 | func (mr *MockInterfaceMockRecorder) AuthorizationV1() *gomock.Call { 228 | mr.mock.ctrl.T.Helper() 229 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthorizationV1", reflect.TypeOf((*MockInterface)(nil).AuthorizationV1)) 230 | } 231 | 232 | // AuthorizationV1beta1 mocks base method 233 | func (m *MockInterface) AuthorizationV1beta1() v1beta12.AuthorizationV1beta1Interface { 234 | m.ctrl.T.Helper() 235 | ret := m.ctrl.Call(m, "AuthorizationV1beta1") 236 | ret0, _ := ret[0].(v1beta12.AuthorizationV1beta1Interface) 237 | return ret0 238 | } 239 | 240 | // AuthorizationV1beta1 indicates an expected call of AuthorizationV1beta1 241 | func (mr *MockInterfaceMockRecorder) AuthorizationV1beta1() *gomock.Call { 242 | mr.mock.ctrl.T.Helper() 243 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthorizationV1beta1", reflect.TypeOf((*MockInterface)(nil).AuthorizationV1beta1)) 244 | } 245 | 246 | // Autoscaling mocks base method 247 | func (m *MockInterface) Autoscaling() v12.AutoscalingV1Interface { 248 | m.ctrl.T.Helper() 249 | ret := m.ctrl.Call(m, "Autoscaling") 250 | ret0, _ := ret[0].(v12.AutoscalingV1Interface) 251 | return ret0 252 | } 253 | 254 | // Autoscaling indicates an expected call of Autoscaling 255 | func (mr *MockInterfaceMockRecorder) Autoscaling() *gomock.Call { 256 | mr.mock.ctrl.T.Helper() 257 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Autoscaling", reflect.TypeOf((*MockInterface)(nil).Autoscaling)) 258 | } 259 | 260 | // AutoscalingV1 mocks base method 261 | func (m *MockInterface) AutoscalingV1() v12.AutoscalingV1Interface { 262 | m.ctrl.T.Helper() 263 | ret := m.ctrl.Call(m, "AutoscalingV1") 264 | ret0, _ := ret[0].(v12.AutoscalingV1Interface) 265 | return ret0 266 | } 267 | 268 | // AutoscalingV1 indicates an expected call of AutoscalingV1 269 | func (mr *MockInterfaceMockRecorder) AutoscalingV1() *gomock.Call { 270 | mr.mock.ctrl.T.Helper() 271 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AutoscalingV1", reflect.TypeOf((*MockInterface)(nil).AutoscalingV1)) 272 | } 273 | 274 | // AutoscalingV2beta1 mocks base method 275 | func (m *MockInterface) AutoscalingV2beta1() v2beta1.AutoscalingV2beta1Interface { 276 | m.ctrl.T.Helper() 277 | ret := m.ctrl.Call(m, "AutoscalingV2beta1") 278 | ret0, _ := ret[0].(v2beta1.AutoscalingV2beta1Interface) 279 | return ret0 280 | } 281 | 282 | // AutoscalingV2beta1 indicates an expected call of AutoscalingV2beta1 283 | func (mr *MockInterfaceMockRecorder) AutoscalingV2beta1() *gomock.Call { 284 | mr.mock.ctrl.T.Helper() 285 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AutoscalingV2beta1", reflect.TypeOf((*MockInterface)(nil).AutoscalingV2beta1)) 286 | } 287 | 288 | // Batch mocks base method 289 | func (m *MockInterface) Batch() v13.BatchV1Interface { 290 | m.ctrl.T.Helper() 291 | ret := m.ctrl.Call(m, "Batch") 292 | ret0, _ := ret[0].(v13.BatchV1Interface) 293 | return ret0 294 | } 295 | 296 | // Batch indicates an expected call of Batch 297 | func (mr *MockInterfaceMockRecorder) Batch() *gomock.Call { 298 | mr.mock.ctrl.T.Helper() 299 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Batch", reflect.TypeOf((*MockInterface)(nil).Batch)) 300 | } 301 | 302 | // BatchV1 mocks base method 303 | func (m *MockInterface) BatchV1() v13.BatchV1Interface { 304 | m.ctrl.T.Helper() 305 | ret := m.ctrl.Call(m, "BatchV1") 306 | ret0, _ := ret[0].(v13.BatchV1Interface) 307 | return ret0 308 | } 309 | 310 | // BatchV1 indicates an expected call of BatchV1 311 | func (mr *MockInterfaceMockRecorder) BatchV1() *gomock.Call { 312 | mr.mock.ctrl.T.Helper() 313 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchV1", reflect.TypeOf((*MockInterface)(nil).BatchV1)) 314 | } 315 | 316 | // BatchV1beta1 mocks base method 317 | func (m *MockInterface) BatchV1beta1() v1beta13.BatchV1beta1Interface { 318 | m.ctrl.T.Helper() 319 | ret := m.ctrl.Call(m, "BatchV1beta1") 320 | ret0, _ := ret[0].(v1beta13.BatchV1beta1Interface) 321 | return ret0 322 | } 323 | 324 | // BatchV1beta1 indicates an expected call of BatchV1beta1 325 | func (mr *MockInterfaceMockRecorder) BatchV1beta1() *gomock.Call { 326 | mr.mock.ctrl.T.Helper() 327 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchV1beta1", reflect.TypeOf((*MockInterface)(nil).BatchV1beta1)) 328 | } 329 | 330 | // BatchV2alpha1 mocks base method 331 | func (m *MockInterface) BatchV2alpha1() v2alpha1.BatchV2alpha1Interface { 332 | m.ctrl.T.Helper() 333 | ret := m.ctrl.Call(m, "BatchV2alpha1") 334 | ret0, _ := ret[0].(v2alpha1.BatchV2alpha1Interface) 335 | return ret0 336 | } 337 | 338 | // BatchV2alpha1 indicates an expected call of BatchV2alpha1 339 | func (mr *MockInterfaceMockRecorder) BatchV2alpha1() *gomock.Call { 340 | mr.mock.ctrl.T.Helper() 341 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchV2alpha1", reflect.TypeOf((*MockInterface)(nil).BatchV2alpha1)) 342 | } 343 | 344 | // Certificates mocks base method 345 | func (m *MockInterface) Certificates() v1beta14.CertificatesV1beta1Interface { 346 | m.ctrl.T.Helper() 347 | ret := m.ctrl.Call(m, "Certificates") 348 | ret0, _ := ret[0].(v1beta14.CertificatesV1beta1Interface) 349 | return ret0 350 | } 351 | 352 | // Certificates indicates an expected call of Certificates 353 | func (mr *MockInterfaceMockRecorder) Certificates() *gomock.Call { 354 | mr.mock.ctrl.T.Helper() 355 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Certificates", reflect.TypeOf((*MockInterface)(nil).Certificates)) 356 | } 357 | 358 | // CertificatesV1beta1 mocks base method 359 | func (m *MockInterface) CertificatesV1beta1() v1beta14.CertificatesV1beta1Interface { 360 | m.ctrl.T.Helper() 361 | ret := m.ctrl.Call(m, "CertificatesV1beta1") 362 | ret0, _ := ret[0].(v1beta14.CertificatesV1beta1Interface) 363 | return ret0 364 | } 365 | 366 | // CertificatesV1beta1 indicates an expected call of CertificatesV1beta1 367 | func (mr *MockInterfaceMockRecorder) CertificatesV1beta1() *gomock.Call { 368 | mr.mock.ctrl.T.Helper() 369 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CertificatesV1beta1", reflect.TypeOf((*MockInterface)(nil).CertificatesV1beta1)) 370 | } 371 | 372 | // Core mocks base method 373 | func (m *MockInterface) Core() v14.CoreV1Interface { 374 | m.ctrl.T.Helper() 375 | ret := m.ctrl.Call(m, "Core") 376 | ret0, _ := ret[0].(v14.CoreV1Interface) 377 | return ret0 378 | } 379 | 380 | // Core indicates an expected call of Core 381 | func (mr *MockInterfaceMockRecorder) Core() *gomock.Call { 382 | mr.mock.ctrl.T.Helper() 383 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Core", reflect.TypeOf((*MockInterface)(nil).Core)) 384 | } 385 | 386 | // CoreV1 mocks base method 387 | func (m *MockInterface) CoreV1() v14.CoreV1Interface { 388 | m.ctrl.T.Helper() 389 | ret := m.ctrl.Call(m, "CoreV1") 390 | ret0, _ := ret[0].(v14.CoreV1Interface) 391 | return ret0 392 | } 393 | 394 | // CoreV1 indicates an expected call of CoreV1 395 | func (mr *MockInterfaceMockRecorder) CoreV1() *gomock.Call { 396 | mr.mock.ctrl.T.Helper() 397 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CoreV1", reflect.TypeOf((*MockInterface)(nil).CoreV1)) 398 | } 399 | 400 | // Discovery mocks base method 401 | func (m *MockInterface) Discovery() discovery.DiscoveryInterface { 402 | m.ctrl.T.Helper() 403 | ret := m.ctrl.Call(m, "Discovery") 404 | ret0, _ := ret[0].(discovery.DiscoveryInterface) 405 | return ret0 406 | } 407 | 408 | // Discovery indicates an expected call of Discovery 409 | func (mr *MockInterfaceMockRecorder) Discovery() *gomock.Call { 410 | mr.mock.ctrl.T.Helper() 411 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Discovery", reflect.TypeOf((*MockInterface)(nil).Discovery)) 412 | } 413 | 414 | // Events mocks base method 415 | func (m *MockInterface) Events() v1beta15.EventsV1beta1Interface { 416 | m.ctrl.T.Helper() 417 | ret := m.ctrl.Call(m, "Events") 418 | ret0, _ := ret[0].(v1beta15.EventsV1beta1Interface) 419 | return ret0 420 | } 421 | 422 | // Events indicates an expected call of Events 423 | func (mr *MockInterfaceMockRecorder) Events() *gomock.Call { 424 | mr.mock.ctrl.T.Helper() 425 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Events", reflect.TypeOf((*MockInterface)(nil).Events)) 426 | } 427 | 428 | // EventsV1beta1 mocks base method 429 | func (m *MockInterface) EventsV1beta1() v1beta15.EventsV1beta1Interface { 430 | m.ctrl.T.Helper() 431 | ret := m.ctrl.Call(m, "EventsV1beta1") 432 | ret0, _ := ret[0].(v1beta15.EventsV1beta1Interface) 433 | return ret0 434 | } 435 | 436 | // EventsV1beta1 indicates an expected call of EventsV1beta1 437 | func (mr *MockInterfaceMockRecorder) EventsV1beta1() *gomock.Call { 438 | mr.mock.ctrl.T.Helper() 439 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EventsV1beta1", reflect.TypeOf((*MockInterface)(nil).EventsV1beta1)) 440 | } 441 | 442 | // Extensions mocks base method 443 | func (m *MockInterface) Extensions() v1beta16.ExtensionsV1beta1Interface { 444 | m.ctrl.T.Helper() 445 | ret := m.ctrl.Call(m, "Extensions") 446 | ret0, _ := ret[0].(v1beta16.ExtensionsV1beta1Interface) 447 | return ret0 448 | } 449 | 450 | // Extensions indicates an expected call of Extensions 451 | func (mr *MockInterfaceMockRecorder) Extensions() *gomock.Call { 452 | mr.mock.ctrl.T.Helper() 453 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Extensions", reflect.TypeOf((*MockInterface)(nil).Extensions)) 454 | } 455 | 456 | // ExtensionsV1beta1 mocks base method 457 | func (m *MockInterface) ExtensionsV1beta1() v1beta16.ExtensionsV1beta1Interface { 458 | m.ctrl.T.Helper() 459 | ret := m.ctrl.Call(m, "ExtensionsV1beta1") 460 | ret0, _ := ret[0].(v1beta16.ExtensionsV1beta1Interface) 461 | return ret0 462 | } 463 | 464 | // ExtensionsV1beta1 indicates an expected call of ExtensionsV1beta1 465 | func (mr *MockInterfaceMockRecorder) ExtensionsV1beta1() *gomock.Call { 466 | mr.mock.ctrl.T.Helper() 467 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExtensionsV1beta1", reflect.TypeOf((*MockInterface)(nil).ExtensionsV1beta1)) 468 | } 469 | 470 | // Networking mocks base method 471 | func (m *MockInterface) Networking() v15.NetworkingV1Interface { 472 | m.ctrl.T.Helper() 473 | ret := m.ctrl.Call(m, "Networking") 474 | ret0, _ := ret[0].(v15.NetworkingV1Interface) 475 | return ret0 476 | } 477 | 478 | // Networking indicates an expected call of Networking 479 | func (mr *MockInterfaceMockRecorder) Networking() *gomock.Call { 480 | mr.mock.ctrl.T.Helper() 481 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Networking", reflect.TypeOf((*MockInterface)(nil).Networking)) 482 | } 483 | 484 | // NetworkingV1 mocks base method 485 | func (m *MockInterface) NetworkingV1() v15.NetworkingV1Interface { 486 | m.ctrl.T.Helper() 487 | ret := m.ctrl.Call(m, "NetworkingV1") 488 | ret0, _ := ret[0].(v15.NetworkingV1Interface) 489 | return ret0 490 | } 491 | 492 | // NetworkingV1 indicates an expected call of NetworkingV1 493 | func (mr *MockInterfaceMockRecorder) NetworkingV1() *gomock.Call { 494 | mr.mock.ctrl.T.Helper() 495 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkingV1", reflect.TypeOf((*MockInterface)(nil).NetworkingV1)) 496 | } 497 | 498 | // Policy mocks base method 499 | func (m *MockInterface) Policy() v1beta17.PolicyV1beta1Interface { 500 | m.ctrl.T.Helper() 501 | ret := m.ctrl.Call(m, "Policy") 502 | ret0, _ := ret[0].(v1beta17.PolicyV1beta1Interface) 503 | return ret0 504 | } 505 | 506 | // Policy indicates an expected call of Policy 507 | func (mr *MockInterfaceMockRecorder) Policy() *gomock.Call { 508 | mr.mock.ctrl.T.Helper() 509 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Policy", reflect.TypeOf((*MockInterface)(nil).Policy)) 510 | } 511 | 512 | // PolicyV1beta1 mocks base method 513 | func (m *MockInterface) PolicyV1beta1() v1beta17.PolicyV1beta1Interface { 514 | m.ctrl.T.Helper() 515 | ret := m.ctrl.Call(m, "PolicyV1beta1") 516 | ret0, _ := ret[0].(v1beta17.PolicyV1beta1Interface) 517 | return ret0 518 | } 519 | 520 | // PolicyV1beta1 indicates an expected call of PolicyV1beta1 521 | func (mr *MockInterfaceMockRecorder) PolicyV1beta1() *gomock.Call { 522 | mr.mock.ctrl.T.Helper() 523 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PolicyV1beta1", reflect.TypeOf((*MockInterface)(nil).PolicyV1beta1)) 524 | } 525 | 526 | // Rbac mocks base method 527 | func (m *MockInterface) Rbac() v16.RbacV1Interface { 528 | m.ctrl.T.Helper() 529 | ret := m.ctrl.Call(m, "Rbac") 530 | ret0, _ := ret[0].(v16.RbacV1Interface) 531 | return ret0 532 | } 533 | 534 | // Rbac indicates an expected call of Rbac 535 | func (mr *MockInterfaceMockRecorder) Rbac() *gomock.Call { 536 | mr.mock.ctrl.T.Helper() 537 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Rbac", reflect.TypeOf((*MockInterface)(nil).Rbac)) 538 | } 539 | 540 | // RbacV1 mocks base method 541 | func (m *MockInterface) RbacV1() v16.RbacV1Interface { 542 | m.ctrl.T.Helper() 543 | ret := m.ctrl.Call(m, "RbacV1") 544 | ret0, _ := ret[0].(v16.RbacV1Interface) 545 | return ret0 546 | } 547 | 548 | // RbacV1 indicates an expected call of RbacV1 549 | func (mr *MockInterfaceMockRecorder) RbacV1() *gomock.Call { 550 | mr.mock.ctrl.T.Helper() 551 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RbacV1", reflect.TypeOf((*MockInterface)(nil).RbacV1)) 552 | } 553 | 554 | // RbacV1alpha1 mocks base method 555 | func (m *MockInterface) RbacV1alpha1() v1alpha10.RbacV1alpha1Interface { 556 | m.ctrl.T.Helper() 557 | ret := m.ctrl.Call(m, "RbacV1alpha1") 558 | ret0, _ := ret[0].(v1alpha10.RbacV1alpha1Interface) 559 | return ret0 560 | } 561 | 562 | // RbacV1alpha1 indicates an expected call of RbacV1alpha1 563 | func (mr *MockInterfaceMockRecorder) RbacV1alpha1() *gomock.Call { 564 | mr.mock.ctrl.T.Helper() 565 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RbacV1alpha1", reflect.TypeOf((*MockInterface)(nil).RbacV1alpha1)) 566 | } 567 | 568 | // RbacV1beta1 mocks base method 569 | func (m *MockInterface) RbacV1beta1() v1beta18.RbacV1beta1Interface { 570 | m.ctrl.T.Helper() 571 | ret := m.ctrl.Call(m, "RbacV1beta1") 572 | ret0, _ := ret[0].(v1beta18.RbacV1beta1Interface) 573 | return ret0 574 | } 575 | 576 | // RbacV1beta1 indicates an expected call of RbacV1beta1 577 | func (mr *MockInterfaceMockRecorder) RbacV1beta1() *gomock.Call { 578 | mr.mock.ctrl.T.Helper() 579 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RbacV1beta1", reflect.TypeOf((*MockInterface)(nil).RbacV1beta1)) 580 | } 581 | 582 | // Scheduling mocks base method 583 | func (m *MockInterface) Scheduling() v1alpha11.SchedulingV1alpha1Interface { 584 | m.ctrl.T.Helper() 585 | ret := m.ctrl.Call(m, "Scheduling") 586 | ret0, _ := ret[0].(v1alpha11.SchedulingV1alpha1Interface) 587 | return ret0 588 | } 589 | 590 | // Scheduling indicates an expected call of Scheduling 591 | func (mr *MockInterfaceMockRecorder) Scheduling() *gomock.Call { 592 | mr.mock.ctrl.T.Helper() 593 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Scheduling", reflect.TypeOf((*MockInterface)(nil).Scheduling)) 594 | } 595 | 596 | // SchedulingV1alpha1 mocks base method 597 | func (m *MockInterface) SchedulingV1alpha1() v1alpha11.SchedulingV1alpha1Interface { 598 | m.ctrl.T.Helper() 599 | ret := m.ctrl.Call(m, "SchedulingV1alpha1") 600 | ret0, _ := ret[0].(v1alpha11.SchedulingV1alpha1Interface) 601 | return ret0 602 | } 603 | 604 | // SchedulingV1alpha1 indicates an expected call of SchedulingV1alpha1 605 | func (mr *MockInterfaceMockRecorder) SchedulingV1alpha1() *gomock.Call { 606 | mr.mock.ctrl.T.Helper() 607 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedulingV1alpha1", reflect.TypeOf((*MockInterface)(nil).SchedulingV1alpha1)) 608 | } 609 | 610 | // Settings mocks base method 611 | func (m *MockInterface) Settings() v1alpha12.SettingsV1alpha1Interface { 612 | m.ctrl.T.Helper() 613 | ret := m.ctrl.Call(m, "Settings") 614 | ret0, _ := ret[0].(v1alpha12.SettingsV1alpha1Interface) 615 | return ret0 616 | } 617 | 618 | // Settings indicates an expected call of Settings 619 | func (mr *MockInterfaceMockRecorder) Settings() *gomock.Call { 620 | mr.mock.ctrl.T.Helper() 621 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Settings", reflect.TypeOf((*MockInterface)(nil).Settings)) 622 | } 623 | 624 | // SettingsV1alpha1 mocks base method 625 | func (m *MockInterface) SettingsV1alpha1() v1alpha12.SettingsV1alpha1Interface { 626 | m.ctrl.T.Helper() 627 | ret := m.ctrl.Call(m, "SettingsV1alpha1") 628 | ret0, _ := ret[0].(v1alpha12.SettingsV1alpha1Interface) 629 | return ret0 630 | } 631 | 632 | // SettingsV1alpha1 indicates an expected call of SettingsV1alpha1 633 | func (mr *MockInterfaceMockRecorder) SettingsV1alpha1() *gomock.Call { 634 | mr.mock.ctrl.T.Helper() 635 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SettingsV1alpha1", reflect.TypeOf((*MockInterface)(nil).SettingsV1alpha1)) 636 | } 637 | 638 | // Storage mocks base method 639 | func (m *MockInterface) Storage() v17.StorageV1Interface { 640 | m.ctrl.T.Helper() 641 | ret := m.ctrl.Call(m, "Storage") 642 | ret0, _ := ret[0].(v17.StorageV1Interface) 643 | return ret0 644 | } 645 | 646 | // Storage indicates an expected call of Storage 647 | func (mr *MockInterfaceMockRecorder) Storage() *gomock.Call { 648 | mr.mock.ctrl.T.Helper() 649 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Storage", reflect.TypeOf((*MockInterface)(nil).Storage)) 650 | } 651 | 652 | // StorageV1 mocks base method 653 | func (m *MockInterface) StorageV1() v17.StorageV1Interface { 654 | m.ctrl.T.Helper() 655 | ret := m.ctrl.Call(m, "StorageV1") 656 | ret0, _ := ret[0].(v17.StorageV1Interface) 657 | return ret0 658 | } 659 | 660 | // StorageV1 indicates an expected call of StorageV1 661 | func (mr *MockInterfaceMockRecorder) StorageV1() *gomock.Call { 662 | mr.mock.ctrl.T.Helper() 663 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageV1", reflect.TypeOf((*MockInterface)(nil).StorageV1)) 664 | } 665 | 666 | // StorageV1alpha1 mocks base method 667 | func (m *MockInterface) StorageV1alpha1() v1alpha13.StorageV1alpha1Interface { 668 | m.ctrl.T.Helper() 669 | ret := m.ctrl.Call(m, "StorageV1alpha1") 670 | ret0, _ := ret[0].(v1alpha13.StorageV1alpha1Interface) 671 | return ret0 672 | } 673 | 674 | // StorageV1alpha1 indicates an expected call of StorageV1alpha1 675 | func (mr *MockInterfaceMockRecorder) StorageV1alpha1() *gomock.Call { 676 | mr.mock.ctrl.T.Helper() 677 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageV1alpha1", reflect.TypeOf((*MockInterface)(nil).StorageV1alpha1)) 678 | } 679 | 680 | // StorageV1beta1 mocks base method 681 | func (m *MockInterface) StorageV1beta1() v1beta19.StorageV1beta1Interface { 682 | m.ctrl.T.Helper() 683 | ret := m.ctrl.Call(m, "StorageV1beta1") 684 | ret0, _ := ret[0].(v1beta19.StorageV1beta1Interface) 685 | return ret0 686 | } 687 | 688 | // StorageV1beta1 indicates an expected call of StorageV1beta1 689 | func (mr *MockInterfaceMockRecorder) StorageV1beta1() *gomock.Call { 690 | mr.mock.ctrl.T.Helper() 691 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageV1beta1", reflect.TypeOf((*MockInterface)(nil).StorageV1beta1)) 692 | } 693 | --------------------------------------------------------------------------------