tools/scaler/scaler.go (94 lines of code) (raw):

// Copyright 2019 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "context" "fmt" "log" "os" "path/filepath" "strings" "time" "github.com/pkg/errors" "gopkg.in/alecthomas/kingpin.v2" appsV1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/runtime" "github.com/prometheus/test-infra/pkg/provider/k8s" ) type scale struct { k8sClient *k8s.K8s min int32 max int32 interval time.Duration } func newScaler() *scale { k, err := k8s.New(context.Background(), nil) if err != nil { fmt.Fprintln(os.Stderr, errors.Wrapf(err, "Error creating k8s client inside the k8s cluster")) os.Exit(2) } return &scale{ k8sClient: k, } } func (s *scale) updateReplicas(replicas *int32) []k8s.Resource { var k8sResource []k8s.Resource for _, deployment := range s.k8sClient.GetResources() { k8sObjects := make([]runtime.Object, 0) for _, resource := range deployment.Objects { if kind := strings.ToLower(resource.GetObjectKind().GroupVersionKind().Kind); kind == "deployment" { req := resource.(*appsV1.Deployment) req.Spec.Replicas = replicas k8sObjects = append(k8sObjects, req.DeepCopyObject()) } } if len(k8sObjects) > 0 { k8sResource = append(k8sResource, k8s.Resource{FileName: deployment.FileName, Objects: k8sObjects}) } } return k8sResource } func (s *scale) scale(*kingpin.ParseContext) error { log.Printf("Starting Prombench-Scaler:\n\t max: %d\n\t min: %d\n\t interval: %s", s.max, s.min, s.interval) maxResourceObjects := s.updateReplicas(&s.max) minResourceObjects := s.updateReplicas(&s.min) for { log.Printf("Scaling Deployment to %d", s.max) if err := s.k8sClient.ResourceApply(maxResourceObjects); err != nil { fmt.Fprintln(os.Stderr, errors.Wrapf(err, "Error scaling deployment")) } time.Sleep(s.interval) log.Printf("Scaling Deployment to %d", s.min) if err := s.k8sClient.ResourceApply(minResourceObjects); err != nil { fmt.Fprintln(os.Stderr, errors.Wrapf(err, "Error scaling deployment")) } time.Sleep(s.interval) } } func main() { app := kingpin.New(filepath.Base(os.Args[0]), "The Prombench-Scaler tool") app.HelpFlag.Short('h') s := newScaler() k8sApp := app.Command("scale", "Scale a Kubernetes deployment object periodically up and down. \nex: ./scaler scale -v NAMESPACE:scale -f fake-webserver.yaml 20 1 15m"). Action(s.k8sClient.DeploymentsParse). Action(s.scale) k8sApp.Flag("file", "yaml file or folder that describes the parameters for the deployment."). Required(). Short('f'). ExistingFilesOrDirsVar(&s.k8sClient.DeploymentFiles) k8sApp.Flag("vars", "When provided it will substitute the token holders in the yaml file. Follows the standard golang template formating - {{ .hashStable }}."). Short('v'). StringMapVar(&s.k8sClient.DeploymentVars) k8sApp.Arg("max", "Number of Replicas to scale up."). Required(). Int32Var(&s.max) k8sApp.Arg("min", "Number of Replicas to scale down."). Required(). Int32Var(&s.min) k8sApp.Arg("interval", "Time to wait before changing the number of replicas."). Required(). DurationVar(&s.interval) if _, err := app.Parse(os.Args[1:]); err != nil { fmt.Fprintln(os.Stderr, errors.Wrapf(err, "Error parsing commandline arguments")) app.Usage(os.Args[1:]) os.Exit(2) } }