faas/gateway/plugin/external.go
Simon Pelczer 7fe67d7af6 Implemented the autoscaling steps to be proportions of the max replicas.
Introduced an new label to set the scaling factor that is used to calculate th proportions, setting it to 0 also allows to disable scaling.
Updated the tests to reflect the changes and added a new test which shows that setting the scaling factor to 0 indeed does disable scaling.
Ensured that the scaling factor is always between [0 and 100].

Signed-off-by: Simon Pelczer <templum.dev@gmail.com>
2018-04-11 19:30:43 -07:00

174 lines
4.1 KiB
Go

// Copyright (c) Alex Ellis 2017. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
package plugin
import (
"bytes"
"encoding/json"
"log"
"net"
"net/http"
"net/url"
"strconv"
"time"
"fmt"
"io/ioutil"
"github.com/openfaas/faas/gateway/handlers"
"github.com/openfaas/faas/gateway/requests"
)
// NewExternalServiceQuery proxies service queries to external plugin via HTTP
func NewExternalServiceQuery(externalURL url.URL) handlers.ServiceQuery {
proxyClient := http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 3 * time.Second,
KeepAlive: 0,
}).DialContext,
MaxIdleConns: 1,
DisableKeepAlives: true,
IdleConnTimeout: 120 * time.Millisecond,
ExpectContinueTimeout: 1500 * time.Millisecond,
},
}
return ExternalServiceQuery{
URL: externalURL,
ProxyClient: proxyClient,
}
}
// ExternalServiceQuery proxies service queries to external plugin via HTTP
type ExternalServiceQuery struct {
URL url.URL
ProxyClient http.Client
}
// ScaleServiceRequest request scaling of replica
type ScaleServiceRequest struct {
ServiceName string `json:"serviceName"`
Replicas uint64 `json:"replicas"`
}
// GetReplicas replica count for function
func (s ExternalServiceQuery) GetReplicas(serviceName string) (handlers.ServiceQueryResponse, error) {
var err error
function := requests.Function{}
urlPath := fmt.Sprintf("%ssystem/function/%s", s.URL.String(), serviceName)
req, _ := http.NewRequest(http.MethodGet, urlPath, nil)
res, err := s.ProxyClient.Do(req)
if err != nil {
log.Println(urlPath, err)
} else {
if res.Body != nil {
defer res.Body.Close()
}
if res.StatusCode == http.StatusOK {
bytesOut, _ := ioutil.ReadAll(res.Body)
err = json.Unmarshal(bytesOut, &function)
if err != nil {
log.Println(urlPath, err)
}
}
}
maxReplicas := uint64(handlers.DefaultMaxReplicas)
minReplicas := uint64(handlers.DefaultMinReplicas)
scalingFactor := uint64(handlers.DefaultScalingFactor)
availableReplicas := function.AvailableReplicas
if function.Labels != nil {
labels := *function.Labels
minScale := labels[handlers.MinScaleLabel]
maxScale := labels[handlers.MaxScaleLabel]
scaleFactor := labels[handlers.ScalingFactorLabel]
if len(minScale) > 0 {
labelValue, err := strconv.Atoi(minScale)
if err != nil {
log.Printf("Bad replica count: %s, should be uint", minScale)
} else {
minReplicas = uint64(labelValue)
}
}
if len(maxScale) > 0 {
labelValue, err := strconv.Atoi(maxScale)
if err != nil {
log.Printf("Bad replica count: %s, should be uint", maxScale)
} else {
maxReplicas = uint64(labelValue)
}
}
if len(scaleFactor) > 0 {
labelValue, err := strconv.Atoi(scaleFactor)
if err != nil {
log.Printf("Bad Scaling Factor: %s, should be uint", scaleFactor)
} else {
var temp = uint64(labelValue)
if temp >= 0 && temp <= 100 {
scalingFactor = temp
} else {
log.Printf("Bad Scaling Factor: %s, is in range [0 - 100]", temp)
}
}
}
}
return handlers.ServiceQueryResponse{
Replicas: function.Replicas,
MaxReplicas: maxReplicas,
MinReplicas: minReplicas,
ScalingFactor: scalingFactor,
AvailableReplicas: availableReplicas,
}, err
}
// SetReplicas update the replica count
func (s ExternalServiceQuery) SetReplicas(serviceName string, count uint64) error {
var err error
scaleReq := ScaleServiceRequest{
ServiceName: serviceName,
Replicas: count,
}
requestBody, err := json.Marshal(scaleReq)
if err != nil {
return err
}
urlPath := fmt.Sprintf("%ssystem/scale-function/%s", s.URL.String(), serviceName)
req, _ := http.NewRequest(http.MethodPost, urlPath, bytes.NewReader(requestBody))
defer req.Body.Close()
res, err := s.ProxyClient.Do(req)
if err != nil {
log.Println(urlPath, err)
} else {
if res.Body != nil {
defer res.Body.Close()
}
}
if res.StatusCode != http.StatusOK {
err = fmt.Errorf("error scaling HTTP code %d, %s", res.StatusCode, urlPath)
}
return err
}