Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add log levels, new log framework, verbosity #93

Merged
merged 2 commits into from Jan 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Support for other entrypoints other than `/bin/sh`, can specify an entrypoint for the shell command method.
- Add logging library `glog` to allow logging at levels of severity and verbosity.
- Can specify verbosity level of logs via the `logVerbosity` configuration option.
### Changed
- Can scale ReplicaSets, ReplicationControllers and StatefulSets alongside Deployments.
- ResourceMetrics fields have `resourceName` and `resource` rather than `deploymentName` and `deployment`. In JSON this means that only the resource name will be exposed via field `resource`.
Expand Down
9 changes: 3 additions & 6 deletions api/api.go
Expand Up @@ -22,7 +22,6 @@ package api
import (
"encoding/json"
"fmt"
"log"
"net/http"

"github.com/go-chi/chi"
Expand Down Expand Up @@ -84,8 +83,7 @@ func (api *API) getMetrics(w http.ResponseWriter, r *http.Request) {
response, err := json.Marshal(metrics)
if err != nil {
// Should not occur, panic
log.Panic(err)
return
panic(err)
}
w.WriteHeader(http.StatusOK)
w.Write(response)
Expand Down Expand Up @@ -125,8 +123,7 @@ func (api *API) getEvaluation(w http.ResponseWriter, r *http.Request) {
response, err := json.Marshal(evaluations)
if err != nil {
// Should not occur, panic
log.Panic(err)
return
panic(err)
}
w.WriteHeader(http.StatusOK)
w.Write(response)
Expand All @@ -153,7 +150,7 @@ func apiError(w http.ResponseWriter, apiErr *Error) {
response, err := json.Marshal(apiErr)
if err != nil {
// Should not occur, panic
log.Panic(err)
panic(err)
}
w.WriteHeader(apiErr.Code)
w.Write(response)
Expand Down
63 changes: 46 additions & 17 deletions cmd/custom-pod-autoscaler/main.go
Expand Up @@ -24,17 +24,20 @@ package main

import (
"context"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"os/signal"
"strconv"
"syscall"
"time"

"github.com/go-chi/chi"
"github.com/golang/glog"
"github.com/jthomperoo/custom-pod-autoscaler/api"
"github.com/jthomperoo/custom-pod-autoscaler/config"
"github.com/jthomperoo/custom-pod-autoscaler/evaluate"
Expand Down Expand Up @@ -66,10 +69,23 @@ const (
startTimeEnvName = "startTime"
minReplicasEnvName = "minReplicas"
maxReplicasEnvName = "maxReplicas"
logVerbosityEnvName = "logVerbosity"
)

const defaultConfig = "/config.yaml"

func init() {
err := flag.Set("logtostderr", "true")
if err != nil {
log.Fatalf("Fail to set log to standard error flag: %s", err)
}
err = flag.Set("v", "0")
if err != nil {
log.Fatalf("Fail to set default log verbosity flag: %s", err)
}
flag.Parse()
}

func main() {
// Read in environment variables
configPath, exists := os.LookupEnv(configEnvName)
Expand All @@ -81,33 +97,47 @@ func main() {
// Read in config file
configFileData, err := ioutil.ReadFile(configPath)
if err != nil {
log.Panic(err)
glog.Fatalf("Fail to read configuration file: %s", err)
}

// Load Custom Pod Autoscaler config
config, err := config.LoadConfig(configFileData, configEnvs)
if err != nil {
log.Panic(err)
glog.Fatalf("Fail to parse configuration: %s", err)
}

// Create the in-cluster Kubernetes config
clusterConfig, err := rest.InClusterConfig()
if err != nil {
log.Panic(err)
glog.Fatalf("Fail to create in-cluster Kubernetes config: %s", err)
}

// Set up clientset
clientset, err := kubernetes.NewForConfig(clusterConfig)
if err != nil {
log.Panic(err)
glog.Fatalf("Fail to set up Kubernetes clientset: %s", err)
}

// Set up dynamic client
dynamicClient, err := dynamic.NewForConfig(clusterConfig)
if err != nil {
log.Panic(err)
glog.Fatalf("Fail to set up Kubernetes dynamic client: %s", err)
}

// Get group resources
groupResources, err := restmapper.GetAPIGroupResources(clientset.Discovery())
if err != nil {
glog.Fatalf("Fail get group resources: %s", err)
}

// Set logging level
err = flag.Lookup("v").Value.Set(strconv.Itoa(int(config.LogVerbosity)))
if err != nil {
glog.Fatalf("Fail to set log verbosity: %s", err)
}

glog.V(1).Infoln("Setting up resources and clients")

// Unstructured converter
unstructuredConverted := runtime.DefaultUnstructuredConverter

Expand All @@ -117,13 +147,6 @@ func main() {
UnstructuredConverter: unstructuredConverted,
}

// Get group resources
groupResources, err := restmapper.GetAPIGroupResources(clientset.Discovery())
if err != nil {
log.Panic(err)
}

// Set up scaling client
scaleClient := scale.New(
clientset.RESTClient(),
restmapper.NewDiscoveryRESTMapper(groupResources),
Expand Down Expand Up @@ -159,6 +182,8 @@ func main() {
},
}

glog.V(1).Infoln("Setting up REST API")

// Set up API
api := &api.API{
Router: chi.NewRouter(),
Expand All @@ -173,15 +198,17 @@ func main() {
Handler: api.Router,
}

glog.V(1).Infoln("Setting up autoscaler")

delayTime := config.StartTime - (time.Now().UTC().UnixNano() / int64(time.Millisecond) % config.StartTime)
delayStartTimer := time.NewTimer(time.Duration(delayTime) * time.Millisecond)

log.Printf("Waiting %d milliseconds before starting autoscaler\n", delayTime)
glog.V(0).Infof("Waiting %d milliseconds before starting autoscaler\n", delayTime)

go func() {
// Wait for delay to start at expected time
<-delayStartTimer.C
log.Println("Starting autoscaler")
glog.V(0).Infoln("Starting autoscaler")
// Set up time ticker with configured interval
ticker := time.NewTicker(time.Duration(config.Interval) * time.Millisecond)
// Set up shutdown channel, which will listen for UNIX shutdown commands
Expand All @@ -204,7 +231,7 @@ func main() {
for {
select {
case <-shutdown:
log.Println("Shutting down...")
glog.V(0).Infoln("Shutting down...")
// Stop API
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
Expand All @@ -213,16 +240,17 @@ func main() {
ticker.Stop()
return
case <-ticker.C:
glog.V(2).Infoln("Running autoscaler")
err := autoscaler.Scale()
if err != nil {
log.Println(err)
glog.Errorln(err)
}
}
}
}()
}()

log.Println("Starting API")
glog.V(0).Infoln("Starting API")
// Start API
srv.ListenAndServe()
}
Expand All @@ -245,6 +273,7 @@ func readEnvVars() map[string]string {
minReplicasEnvName,
maxReplicasEnvName,
startTimeEnvName,
logVerbosityEnvName,
}
configEnvs := map[string]string{}
for _, envName := range configEnvsNames {
Expand Down
4 changes: 2 additions & 2 deletions codecov.yml
Expand Up @@ -9,11 +9,11 @@ coverage:
status:
project:
default:
target: auto
target: 80
threshold: 1%
patch:
default:
target: auto
target: 80
threshold: 1%
changes: no

Expand Down
18 changes: 10 additions & 8 deletions config/config.go
Expand Up @@ -39,14 +39,15 @@ const (
)

const (
defaultInterval = 15000
defaultHost = "0.0.0.0"
defaultPort = 5000
defaultNamespace = "default"
defaultMinReplicas = 1
defaultMaxReplicas = 10
defaultStartTime = 1
defaultRunMode = PerPodRunMode
defaultInterval = 15000
defaultHost = "0.0.0.0"
defaultPort = 5000
defaultNamespace = "default"
defaultMinReplicas = 1
defaultMaxReplicas = 10
defaultStartTime = 1
defaultRunMode = PerPodRunMode
defaultLogVerbosity = 0
)

const jsonStructTag = "json"
Expand All @@ -64,6 +65,7 @@ type Config struct {
MaxReplicas int32 `json:"maxReplicas"`
RunMode string `json:"runMode"`
StartTime int64 `json:"startTime"`
LogVerbosity int32 `json:"logVerbosity"`
}

// Method describes a method for passing data/triggerering logic, such as through a shell
Expand Down
14 changes: 13 additions & 1 deletion config/config_test.go
Expand Up @@ -38,6 +38,7 @@ const (
defaultMaxReplicas = 10
defaultStartTime = 1
defaultRunMode = "per-pod"
defaultLogVerbosity = 0
)

func TestLoadConfig(t *testing.T) {
Expand Down Expand Up @@ -101,6 +102,7 @@ func TestLoadConfig(t *testing.T) {
MinReplicas: defaultMinReplicas,
MaxReplicas: defaultMaxReplicas,
StartTime: defaultStartTime,
LogVerbosity: defaultLogVerbosity,
ScaleTargetRef: nil,
},
},
Expand All @@ -125,6 +127,7 @@ func TestLoadConfig(t *testing.T) {
"maxReplicas": "6",
"startTime": "0",
"scaleTargetRef": `{ "name": "test target name", "kind": "test target kind", "apiVersion": "test target api version"}`,
"logVerbosity": "3",
},
nil,
&config.Config{
Expand All @@ -149,6 +152,7 @@ func TestLoadConfig(t *testing.T) {
Kind: "test target kind",
APIVersion: "test target api version",
},
LogVerbosity: 3,
},
},
{
Expand All @@ -174,6 +178,7 @@ func TestLoadConfig(t *testing.T) {
maxReplicas: 7
startTime: 0
namespace: "test yaml namespace"
logVerbosity: 2
`, "\t", "", -1)),
nil,
nil,
Expand Down Expand Up @@ -202,6 +207,7 @@ func TestLoadConfig(t *testing.T) {
Entrypoint: "testentry",
},
},
LogVerbosity: 2,
},
},
{
Expand Down Expand Up @@ -230,7 +236,8 @@ func TestLoadConfig(t *testing.T) {
"minReplicas":2,
"maxReplicas":7,
"startTime":0,
"namespace":"test yaml namespace"
"namespace":"test yaml namespace",
"logVerbosity":1
}`),
nil,
nil,
Expand Down Expand Up @@ -259,6 +266,7 @@ func TestLoadConfig(t *testing.T) {
Entrypoint: "testentry",
},
},
LogVerbosity: 1,
},
},
{
Expand Down Expand Up @@ -286,6 +294,7 @@ func TestLoadConfig(t *testing.T) {
"minReplicas": "3",
"maxReplicas": "6",
"startTime": "0",
"logVerbosity": "5",
"scaleTargetRef": `{ "name": "test target name", "kind": "test target kind", "apiVersion": "test target api version"}`,
},
nil,
Expand Down Expand Up @@ -318,6 +327,7 @@ func TestLoadConfig(t *testing.T) {
Entrypoint: "testentry",
},
},
LogVerbosity: 5,
},
},
{
Expand Down Expand Up @@ -350,6 +360,7 @@ func TestLoadConfig(t *testing.T) {
"maxReplicas": "6",
"startTime": "0",
"scaleTargetRef": `{ "name": "test target name", "kind": "test target kind", "apiVersion": "test target api version"}`,
"logVerbosity": "3",
},
nil,
&config.Config{
Expand Down Expand Up @@ -382,6 +393,7 @@ func TestLoadConfig(t *testing.T) {
Entrypoint: "testentry",
},
},
LogVerbosity: 3,
},
},
}
Expand Down
13 changes: 13 additions & 0 deletions docs/reference/configuration.md
Expand Up @@ -158,3 +158,16 @@ Default value: `1`
This defines in milliseconds a starting point for the scaler, with the scaler running as if it started at the time provided. Allows specifying that the autoscaler must start on a multiple of the interval from the start time. For example, a startTime of `60000` would result in the autoscaler starting at the next full minute. The default value will start the autoscaler after a single millisecond, close to instantly.

> Note: The scaling will not actually start until after one interval has passed.
## logVerbosity
Example:
```yaml
logVerbosity: 0
```
Default value: `0`
This defines the verbosity of the logging, allowing for debugging errors/issues. Logging will occur for all values ABOVE and including the verbosity level set.
Log levels:

* `0` - normal.
* `1` - verbose.
* `2` - more verbose around high level logic such as autoscaling/rest api.
* `3` - more verbose around lower level logic such as metric gathering and evaluation.