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 flag to disable metrics and introspection #433

Merged
merged 1 commit into from
May 1, 2019
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 111 additions & 62 deletions README.md

Large diffs are not rendered by default.

66 changes: 43 additions & 23 deletions ipamd/introspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,22 @@ package ipamd
import (
"encoding/json"
"net/http"
"os"
"strconv"
"sync"
"time"

log "github.com/cihub/seelog"
"github.com/prometheus/client_golang/prometheus/promhttp"

"github.com/aws/amazon-vpc-cni-k8s/pkg/networkutils"
"github.com/aws/amazon-vpc-cni-k8s/pkg/utils"
log "github.com/cihub/seelog"
)

const (
// IntrospectionPort is the port for ipamd introspection
IntrospectionPort = 61678
// introspectionAddress is listening on localhost 61679 for ipamd introspection
introspectionAddress = "127.0.0.1:61679"

// Environment variable to disable the introspection endpoints
envDisableIntrospection = "DISABLE_INTROSPECTION"
)

type rootResponse struct {
Expand All @@ -42,29 +44,33 @@ type LoggingHandler struct {
}

func (lh LoggingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Info("Handling http request", "method", r.Method, "from", r.RemoteAddr, "uri", r.RequestURI)
log.Info("Handling http request: ", ", method: ", r.Method, ", from: ", r.RemoteAddr, ", URI: ", r.RequestURI)
lh.h.ServeHTTP(w, r)
}

// SetupHTTP sets up ipamd introspection service endpoint
func (c *IPAMContext) SetupHTTP() {
server := c.setupServer()
// ServeIntrospection sets up ipamd introspection endpoints
func (c *IPAMContext) ServeIntrospection() {
if disableIntrospection() {
log.Info("Introspection endpoints disabled")
return
}

log.Info("Serving introspection endpoints on ", introspectionAddress)
server := c.setupIntrospectionServer()
for {
once := sync.Once{}
utils.RetryWithBackoff(utils.NewSimpleBackoff(time.Second, time.Minute, 0.2, 2), func() error {
// TODO, make this cancellable and use the passed in context; for
// now, not critical if this gets interrupted
_ = utils.RetryWithBackoff(utils.NewSimpleBackoff(time.Second, time.Minute, 0.2, 2), func() error {
err := server.ListenAndServe()
once.Do(func() {
log.Error("Error running http api", "err", err)
log.Error("Error running http API: ", err)
})
return err
})
}
}

func (c *IPAMContext) setupServer() *http.Server {
func (c *IPAMContext) setupIntrospectionServer() *http.Server {
// If enabled, add introspection endpoints
serverFunctions := map[string]func(w http.ResponseWriter, r *http.Request){
"/v1/enis": eniV1RequestHandler(c),
"/v1/eni-configs": eniConfigRequestHandler(c),
Expand All @@ -81,26 +87,24 @@ func (c *IPAMContext) setupServer() *http.Server {
availableCommandResponse, err := json.Marshal(&availableCommands)

if err != nil {
log.Error("Failed to Marshal: %v", err)
log.Error("Failed to marshal: %v", err)
}

defaultHandler := func(w http.ResponseWriter, r *http.Request) {
logErr(w.Write(availableCommandResponse))
}

serveMux := http.NewServeMux()
serveMux.HandleFunc("/", defaultHandler)
for key, fn := range serverFunctions {
serveMux.HandleFunc(key, fn)
}
serveMux.Handle("/metrics", promhttp.Handler())

// Log all requests and then pass through to serveMux
loggingServeMux := http.NewServeMux()
loggingServeMux.Handle("/", LoggingHandler{serveMux})

server := &http.Server{
Addr: ":" + strconv.Itoa(IntrospectionPort),
Addr: introspectionAddress,
Handler: loggingServeMux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
Expand All @@ -112,7 +116,7 @@ func eniV1RequestHandler(ipam *IPAMContext) func(http.ResponseWriter, *http.Requ
return func(w http.ResponseWriter, r *http.Request) {
responseJSON, err := json.Marshal(ipam.dataStore.GetENIInfos())
if err != nil {
log.Error("Failed to marshal ENI data: %v", err)
log.Errorf("Failed to marshal ENI data: %v", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
Expand All @@ -124,7 +128,7 @@ func podV1RequestHandler(ipam *IPAMContext) func(http.ResponseWriter, *http.Requ
return func(w http.ResponseWriter, r *http.Request) {
responseJSON, err := json.Marshal(ipam.dataStore.GetPodInfos())
if err != nil {
log.Error("Failed to marshal pod data: %v", err)
log.Errorf("Failed to marshal pod data: %v", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
Expand All @@ -136,7 +140,7 @@ func eniConfigRequestHandler(ipam *IPAMContext) func(http.ResponseWriter, *http.
return func(w http.ResponseWriter, r *http.Request) {
responseJSON, err := json.Marshal(ipam.eniConfig.Getter())
if err != nil {
log.Error("Failed to marshal ENI config: %v", err)
log.Errorf("Failed to marshal ENI config: %v", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
Expand All @@ -148,7 +152,7 @@ func networkEnvV1RequestHandler() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
responseJSON, err := json.Marshal(networkutils.GetConfigForDebug())
if err != nil {
log.Error("Failed to marshal network env var data: %v", err)
log.Errorf("Failed to marshal network env var data: %v", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
Expand All @@ -160,7 +164,7 @@ func ipamdEnvV1RequestHandler() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
responseJSON, err := json.Marshal(GetConfigForDebug())
if err != nil {
log.Error("Failed to marshal ipamd env var data: %v", err)
log.Errorf("Failed to marshal ipamd env var data: %v", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
Expand All @@ -173,3 +177,19 @@ func logErr(_ int, err error) {
log.Errorf("Write failed: %v", err)
}
}

// disableIntrospection returns true if we should disable the introspection
func disableIntrospection() bool {
return getEnvBoolWithDefault(envDisableIntrospection, false)
}

func getEnvBoolWithDefault(envName string, def bool) bool {
if strValue := os.Getenv(envName); strValue != "" {
parsedValue, err := strconv.ParseBool(strValue)
if err == nil {
return parsedValue
}
log.Errorf("Failed to parse %s, using default `%t`: %v", envName, def, err.Error())
}
return def
}
41 changes: 19 additions & 22 deletions ipamd/ipamd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ const (
primaryENIid = "eni-00000000"
secENIid = "eni-00000001"
testAttachmentID = "eni-00000000-attach"
eniID = "eni-5731da78"
primaryMAC = "12:ef:2a:98:e5:5a"
secMAC = "12:ef:2a:98:e5:5b"
primaryDevice = 0
Expand All @@ -51,10 +50,8 @@ const (
secSubnet = "10.10.20.0/24"
ipaddr01 = "10.10.10.11"
ipaddr02 = "10.10.10.12"
ipaddr03 = "10.10.10.13"
ipaddr11 = "10.10.20.11"
ipaddr12 = "10.10.20.12"
ipaddr13 = "10.10.20.13"
vpcCIDR = "10.10.0.0/16"
)

Expand Down Expand Up @@ -163,12 +160,12 @@ func TestNodeInit(t *testing.T) {
}

func TestIncreaseIPPoolDefault(t *testing.T) {
os.Unsetenv(envCustomNetworkCfg)
_ = os.Unsetenv(envCustomNetworkCfg)
testIncreaseIPPool(t, false)
}

func TestIncreaseIPPoolCustomENI(t *testing.T) {
os.Setenv(envCustomNetworkCfg, "true")
_ = os.Setenv(envCustomNetworkCfg, "true")
testIncreaseIPPool(t, true)
}

Expand Down Expand Up @@ -337,15 +334,15 @@ func TestGetWarmENITarget(t *testing.T) {
ctrl, _, _, _, _, _ := setup(t)
defer ctrl.Finish()

os.Setenv("WARM_IP_TARGET", "5")
_ = os.Setenv("WARM_IP_TARGET", "5")
warmIPTarget := getWarmIPTarget()
assert.Equal(t, warmIPTarget, 5)

os.Unsetenv("WARM_IP_TARGET")
_ = os.Unsetenv("WARM_IP_TARGET")
warmIPTarget = getWarmIPTarget()
assert.Equal(t, warmIPTarget, noWarmIPTarget)

os.Setenv("WARM_IP_TARGET", "non-integer-string")
_ = os.Setenv("WARM_IP_TARGET", "non-integer-string")
warmIPTarget = getWarmIPTarget()
assert.Equal(t, warmIPTarget, noWarmIPTarget)
}
Expand All @@ -355,32 +352,32 @@ func TestGetMaxENI(t *testing.T) {
defer ctrl.Finish()

// MaxENI 5 is less than upper bound of 10, so 5
os.Setenv("MAX_ENI", "5")
_ = os.Setenv("MAX_ENI", "5")
maxENI := getMaxENI(10)
assert.Equal(t, maxENI, 5)

// MaxENI 5 is greater than upper bound of 4, so 4
os.Setenv("MAX_ENI", "5")
_ = os.Setenv("MAX_ENI", "5")
maxENI = getMaxENI(4)
assert.Equal(t, maxENI, 4)

// MaxENI 0 is 0, which means disabled; so use upper bound
os.Setenv("MAX_ENI", "0")
_ = os.Setenv("MAX_ENI", "0")
maxENI = getMaxENI(4)
assert.Equal(t, maxENI, 4)

// MaxENI 1 is less than upper bound of 4, so 1.
os.Setenv("MAX_ENI", "1")
_ = os.Setenv("MAX_ENI", "1")
maxENI = getMaxENI(4)
assert.Equal(t, maxENI, 1)

// Empty MaxENI means disabled, so use upper bound
os.Unsetenv("MAX_ENI")
_ = os.Unsetenv("MAX_ENI")
maxENI = getMaxENI(10)
assert.Equal(t, maxENI, 10)

// Invalid MaxENI means disabled, so use upper bound
os.Setenv("MAX_ENI", "non-integer-string")
_ = os.Setenv("MAX_ENI", "non-integer-string")
maxENI = getMaxENI(10)
assert.Equal(t, maxENI, 10)
}
Expand All @@ -398,30 +395,30 @@ func TestGetWarmIPTargetState(t *testing.T) {

mockContext.dataStore = datastore.NewDataStore()

os.Unsetenv("WARM_IP_TARGET")
_ = os.Unsetenv("WARM_IP_TARGET")
_, _, warmIPTargetDefined := mockContext.ipTargetState()
assert.False(t, warmIPTargetDefined)

os.Setenv("WARM_IP_TARGET", "5")
_ = os.Setenv("WARM_IP_TARGET", "5")
short, over, warmIPTargetDefined := mockContext.ipTargetState()
assert.True(t, warmIPTargetDefined)
assert.Equal(t, 5, short)
assert.Equal(t, 0, over)

// add 2 addresses to datastore
mockContext.dataStore.AddENI("eni-1", 1, true)
mockContext.dataStore.AddIPv4AddressFromStore("eni-1", "1.1.1.1")
mockContext.dataStore.AddIPv4AddressFromStore("eni-1", "1.1.1.2")
_ = mockContext.dataStore.AddENI("eni-1", 1, true)
_ = mockContext.dataStore.AddIPv4AddressFromStore("eni-1", "1.1.1.1")
_ = mockContext.dataStore.AddIPv4AddressFromStore("eni-1", "1.1.1.2")

short, over, warmIPTargetDefined = mockContext.ipTargetState()
assert.True(t, warmIPTargetDefined)
assert.Equal(t, 3, short)
assert.Equal(t, 0, over)

// add 3 more addresses to datastore
mockContext.dataStore.AddIPv4AddressFromStore("eni-1", "1.1.1.3")
mockContext.dataStore.AddIPv4AddressFromStore("eni-1", "1.1.1.4")
mockContext.dataStore.AddIPv4AddressFromStore("eni-1", "1.1.1.5")
_ = mockContext.dataStore.AddIPv4AddressFromStore("eni-1", "1.1.1.3")
_ = mockContext.dataStore.AddIPv4AddressFromStore("eni-1", "1.1.1.4")
_ = mockContext.dataStore.AddIPv4AddressFromStore("eni-1", "1.1.1.5")

short, over, warmIPTargetDefined = mockContext.ipTargetState()
assert.True(t, warmIPTargetDefined)
Expand Down
73 changes: 73 additions & 0 deletions ipamd/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 ipamd

import (
"net/http"
"strconv"
"sync"
"time"

log "github.com/cihub/seelog"
"github.com/prometheus/client_golang/prometheus/promhttp"

"github.com/aws/amazon-vpc-cni-k8s/pkg/utils"
)

const (
// metricsPort is the port for prometheus metrics
metricsPort = 61678

// Environment variable to disable the metrics endpoint on 61678
envDisableMetrics = "DISABLE_METRICS"
)

// ServeMetrics sets up ipamd metrics and introspection endpoints
func (c *IPAMContext) ServeMetrics() {
if disableMetrics() {
log.Info("Metrics endpoint disabled")
return
}

log.Info("Serving metrics on port ", metricsPort)
server := c.setupMetricsServer()
for {
once := sync.Once{}
_ = utils.RetryWithBackoff(utils.NewSimpleBackoff(time.Second, time.Minute, 0.2, 2), func() error {
err := server.ListenAndServe()
once.Do(func() {
log.Error("Error running http API: ", err)
})
return err
})
}
}

func (c *IPAMContext) setupMetricsServer() *http.Server {
// Always add the metrics endpoint
serveMux := http.NewServeMux()
serveMux.Handle("/metrics", promhttp.Handler())
server := &http.Server{
Addr: ":" + strconv.Itoa(metricsPort),
Handler: serveMux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
}
return server
}

// disableMetrics returns true if we should disable metrics
func disableMetrics() bool {
return getEnvBoolWithDefault(envDisableMetrics, false)
}
4 changes: 2 additions & 2 deletions ipamd/rpc_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
)

const (
port = "127.0.0.1:50051"
ipamdgRPCaddress = "127.0.0.1:50051"
)

type server struct {
Expand Down Expand Up @@ -91,7 +91,7 @@ func (s *server) DelNetwork(ctx context.Context, in *pb.DelNetworkRequest) (*pb.

// RunRPCHandler handles request from gRPC
func (c *IPAMContext) RunRPCHandler() error {
lis, err := net.Listen("tcp", port)
lis, err := net.Listen("tcp", ipamdgRPCaddress)
if err != nil {
log.Errorf("Failed to listen gRPC port: %v", err)
return errors.Wrap(err, "ipamd: failed to listen to gRPC port")
Expand Down
Loading