Skip to content

Commit

Permalink
Merge pull request kubernetes#3989 from brett-elliott/useragent
Browse files Browse the repository at this point in the history
Set cluster autoscaler-specific user agent.
  • Loading branch information
k8s-ci-robot authored and Evan Sheng committed Mar 29, 2022
1 parent 2a037da commit 4143a2b
Show file tree
Hide file tree
Showing 10 changed files with 105 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@ type autoscalingGceClientV1 struct {
}

// NewAutoscalingGceClientV1 creates a new client for communicating with GCE v1 API.
func NewAutoscalingGceClientV1(client *http.Client, projectId string) (*autoscalingGceClientV1, error) {
func NewAutoscalingGceClientV1(client *http.Client, projectId string, userAgent string) (*autoscalingGceClientV1, error) {
gceService, err := gce.New(client)
if err != nil {
return nil, err
}
gceService.UserAgent = userAgent

return &autoscalingGceClientV1{
projectId: projectId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ import (
gce_api "google.golang.org/api/compute/v1"
)

func newTestAutoscalingGceClient(t *testing.T, projectId, url string) *autoscalingGceClientV1 {
func newTestAutoscalingGceClient(t *testing.T, projectId, url, userAgent string) *autoscalingGceClientV1 {
client := &http.Client{}
gceClient, err := NewAutoscalingGceClientV1(client, projectId)
gceClient, err := NewAutoscalingGceClientV1(client, projectId, userAgent)
if !assert.NoError(t, err) {
t.Fatalf("fatal error: %v", err)
}
Expand Down Expand Up @@ -63,7 +63,7 @@ const operationDoneResponse = `{
func TestWaitForOp(t *testing.T) {
server := test_util.NewHttpServerMock()
defer server.Close()
g := newTestAutoscalingGceClient(t, "project1", server.URL)
g := newTestAutoscalingGceClient(t, "project1", server.URL, "")

g.operationPollInterval = 1 * time.Millisecond
g.operationWaitTimeout = 500 * time.Millisecond
Expand All @@ -81,7 +81,7 @@ func TestWaitForOp(t *testing.T) {
func TestWaitForOpTimeout(t *testing.T) {
server := test_util.NewHttpServerMock()
defer server.Close()
g := newTestAutoscalingGceClient(t, "project1", server.URL)
g := newTestAutoscalingGceClient(t, "project1", server.URL, "")

// The values here are higher than in other tests since we're aiming for timeout.
// Lower values make this fragile and flakey.
Expand All @@ -97,3 +97,18 @@ func TestWaitForOpTimeout(t *testing.T) {
err := g.waitForOp(operation, projectId, zoneB)
assert.Error(t, err)
}

func TestUserAgent(t *testing.T) {
server := test_util.NewHttpServerMock(test_util.MockFieldUserAgent, test_util.MockFieldResponse)
defer server.Close()
g := newTestAutoscalingGceClient(t, "project1", server.URL, "testuseragent")

g.operationPollInterval = 10 * time.Millisecond
g.operationWaitTimeout = 49 * time.Millisecond

server.On("handle", "/project1/zones/us-central1-b/operations/operation-1505728466148-d16f5197").Return("testuseragent", operationRunningResponse).Maybe()

operation := &gce_api.Operation{Name: "operation-1505728466148-d16f5197"}

g.waitForOp(operation, projectId, zoneB)
}
2 changes: 1 addition & 1 deletion cluster-autoscaler/cloudprovider/gce/gce_cloud_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ func BuildGCE(opts config.AutoscalingOptions, do cloudprovider.NodeGroupDiscover
defer config.Close()
}

manager, err := CreateGceManager(config, do, opts.Regional)
manager, err := CreateGceManager(config, do, opts.Regional, opts.UserAgent)
if err != nil {
klog.Fatalf("Failed to create GCE Manager: %v", err)
}
Expand Down
4 changes: 2 additions & 2 deletions cluster-autoscaler/cloudprovider/gce/gce_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ type gceManagerImpl struct {
}

// CreateGceManager constructs GceManager object.
func CreateGceManager(configReader io.Reader, discoveryOpts cloudprovider.NodeGroupDiscoveryOptions, regional bool) (GceManager, error) {
func CreateGceManager(configReader io.Reader, discoveryOpts cloudprovider.NodeGroupDiscoveryOptions, regional bool, userAgent string) (GceManager, error) {
// Create Google Compute Engine token.
var err error
tokenSource := google.ComputeTokenSource("")
Expand Down Expand Up @@ -163,7 +163,7 @@ func CreateGceManager(configReader io.Reader, discoveryOpts cloudprovider.NodeGr
// Create Google Compute Engine service.
client := oauth2.NewClient(oauth2.NoContext, tokenSource)
client.Timeout = httpTimeout
gceService, err := NewAutoscalingGceClientV1(client, projectId)
gceService, err := NewAutoscalingGceClientV1(client, projectId, userAgent)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion cluster-autoscaler/cloudprovider/gce/gce_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ func buildListInstanceGroupManagersResponse(listInstanceGroupManagerResponsePart
}

func newTestGceManager(t *testing.T, testServerURL string, regional bool) *gceManagerImpl {
gceService := newTestAutoscalingGceClient(t, projectId, testServerURL)
gceService := newTestAutoscalingGceClient(t, projectId, testServerURL, "")

// Override wait for op timeouts.
gceService.operationWaitTimeout = 50 * time.Millisecond
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,17 @@ func newTestPacketManagerRest(t *testing.T, url string) *packetManagerRest {
}
func TestListPacketDevices(t *testing.T) {
var m *packetManagerRest
server := NewHttpServerMock()
server := NewHttpServerMock(MockFieldContentType, MockFieldResponse)
defer server.Close()
if len(os.Getenv("PACKET_AUTH_TOKEN")) > 0 {
// If auth token set in env, hit the actual Packet API
m = newTestPacketManagerRest(t, "https://api.packet.net")
} else {
// Set up a mock Packet API
m = newTestPacketManagerRest(t, server.URL)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return(listPacketDevicesResponse).Times(2)
t.Logf("server URL: %v", server.URL)
t.Logf("default packetManagerNodePool baseURL: %v", m.packetManagerNodePools["default"].baseURL)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return("application/json", listPacketDevicesResponse).Times(2)
}

_, err := m.listPacketDevices()
Expand Down
19 changes: 10 additions & 9 deletions cluster-autoscaler/cloudprovider/packet/packet_node_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const getPacketDeviceResponsePool3 = `

func TestIncreaseDecreaseSize(t *testing.T) {
var m *packetManagerRest
server := NewHttpServerMock()
server := NewHttpServerMock(MockFieldContentType, MockFieldResponse)
defer server.Close()
assert.Equal(t, true, true)
if len(os.Getenv("PACKET_AUTH_TOKEN")) > 0 {
Expand All @@ -47,14 +47,15 @@ func TestIncreaseDecreaseSize(t *testing.T) {
} else {
// Set up a mock Packet API
m = newTestPacketManagerRest(t, server.URL)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return(listPacketDevicesResponse).Times(4)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return(listPacketDevicesResponseAfterIncreasePool3).Times(3)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return(listPacketDevicesResponseAfterIncreasePool2).Times(1)
server.On("handle", "/devices/0f5609af-1c27-451b-8edd-a1283f2c9440").Return(getPacketDeviceResponsePool2).Times(1)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return(listPacketDevicesResponseAfterIncreasePool2).Times(4)
server.On("handle", "/devices/8fa90049-e715-4794-ba31-81c1c78cee84").Return(getPacketDeviceResponsePool3).Times(1)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return(listPacketDevicesResponseAfterIncreasePool2).Times(2)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return(listPacketDevicesResponse).Times(2)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return("application/json", listPacketDevicesResponse).Times(3)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return("application/json", createPacketDeviceResponsePool3).Times(1)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return("application/json", listPacketDevicesResponseAfterIncreasePool3).Times(2)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return("application/json", createPacketDeviceResponsePool2).Times(1)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return("application/json", listPacketDevicesResponseAfterIncreasePool2).Times(3)
server.On("handle", "/devices/0f5609af-1c27-451b-8edd-a1283f2c9440").Return("application/json", deletePacketDeviceResponsePool2).Times(1)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return("application/json", listPacketDevicesResponseAfterIncreasePool3).Times(3)
server.On("handle", "/devices/8fa90049-e715-4794-ba31-81c1c78cee84").Return("application/json", deletePacketDeviceResponsePool3).Times(1)
server.On("handle", "/projects/"+m.packetManagerNodePools["default"].projectID+"/devices").Return("application/json", listPacketDevicesResponse).Times(3)
}
clusterUpdateLock := sync.Mutex{}
ngPool2 := &packetNodeGroup{
Expand Down
2 changes: 2 additions & 0 deletions cluster-autoscaler/config/autoscaling_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,6 @@ type AutoscalingOptions struct {
ClusterAPICloudConfigAuthoritative bool
// Enable or disable cordon nodes functionality before terminating the node during downscale process
CordonNodeBeforeTerminate bool
// User agent to use for HTTP calls.
UserAgent string
}
2 changes: 2 additions & 0 deletions cluster-autoscaler/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ var (
enableProfiling = flag.Bool("profiling", false, "Is debug/pprof endpoint enabled")
clusterAPICloudConfigAuthoritative = flag.Bool("clusterapi-cloud-config-authoritative", false, "Treat the cloud-config flag authoritatively (do not fallback to using kubeconfig flag). ClusterAPI only")
cordonNodeBeforeTerminate = flag.Bool("cordon-node-before-terminating", false, "Should CA cordon nodes before terminating during downscale process")
userAgent = flag.String("user-agent", "cluster-autoscaler", "User agent used for HTTP calls.")
)

func createAutoscalingOptions() config.AutoscalingOptions {
Expand Down Expand Up @@ -249,6 +250,7 @@ func createAutoscalingOptions() config.AutoscalingOptions {
AWSUseStaticInstanceList: *awsUseStaticInstanceList,
ClusterAPICloudConfigAuthoritative: *clusterAPICloudConfigAuthoritative,
CordonNodeBeforeTerminate: *cordonNodeBeforeTerminate,
UserAgent: *userAgent,
}
}

Expand Down
72 changes: 62 additions & 10 deletions cluster-autoscaler/utils/test/test_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,16 @@ package test

import (
"fmt"
"time"

"net/http"
"net/http/httptest"
"strings"
"time"

"github.com/stretchr/testify/mock"
apiv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"

"github.com/stretchr/testify/mock"
)

// BuildTestPod creates a pod with specified resources.
Expand Down Expand Up @@ -213,27 +212,80 @@ func boolptr(val bool) *bool {
// instances, err := g.GetManagedInstances()
// // Check if expected calls were executed.
// mock.AssertExpectationsForObjects(t, server)
//
// Note: to provide a content type, you may pass in the desired
// fields:
// server := NewHttpServerMock(MockFieldContentType, MockFieldResponse)
// ...
// server.On("handle", "/project1/zones/us-central1-b/listManagedInstances").Return("<content type>", "<response>").Once()
// The order of the return objects must match that of the HttpServerMockField constants passed to NewHttpServerMock()
type HttpServerMock struct {
mock.Mock
*httptest.Server
fields []HttpServerMockField
}

// HttpServerMockField specifies a type of field.
type HttpServerMockField int

const (
// MockFieldResponse represents a string response.
MockFieldResponse HttpServerMockField = iota
// MockFieldStatusCode represents an integer HTTP response code.
MockFieldStatusCode
// MockFieldContentType represents a string content type.
MockFieldContentType
// MockFieldUserAgent represents a string user agent.
MockFieldUserAgent
)

// NewHttpServerMock creates new HttpServerMock.
func NewHttpServerMock() *HttpServerMock {
httpServerMock := &HttpServerMock{}
func NewHttpServerMock(fields ...HttpServerMockField) *HttpServerMock {
if len(fields) == 0 {
fields = []HttpServerMockField{MockFieldResponse}
}
foundResponse := false
for _, field := range fields {
if field == MockFieldResponse {
foundResponse = true
break
}
}
if !foundResponse {
panic("Must use MockFieldResponse.")
}
httpServerMock := &HttpServerMock{fields: fields}
mux := http.NewServeMux()
mux.HandleFunc("/",
func(w http.ResponseWriter, req *http.Request) {
result := httpServerMock.handle(req.URL.Path)
w.Write([]byte(result))
result := httpServerMock.handle(req, w, httpServerMock)
_, _ = w.Write([]byte(result))
})

server := httptest.NewServer(mux)
httpServerMock.Server = server
return httpServerMock
}

func (l *HttpServerMock) handle(url string) string {
func (l *HttpServerMock) handle(req *http.Request, w http.ResponseWriter, serverMock *HttpServerMock) string {
url := req.URL.Path
var response string
args := l.Called(url)
return args.String(0)
for i, field := range l.fields {
switch field {
case MockFieldResponse:
response = args.String(i)
case MockFieldContentType:
w.Header().Set("Content-Type", args.String(i))
case MockFieldStatusCode:
w.WriteHeader(args.Int(i))
case MockFieldUserAgent:
gotUserAgent := req.UserAgent()
expectedUserAgent := args.String(i)
if !strings.Contains(gotUserAgent, expectedUserAgent) {
panic(fmt.Sprintf("Error handling URL %s, expected user agent %s but got %s.", url, expectedUserAgent, gotUserAgent))
}
}
}
return response
}

0 comments on commit 4143a2b

Please sign in to comment.