Skip to content

Commit

Permalink
Merge pull request kubernetes#4129 from smarterclayton/add_user_agent
Browse files Browse the repository at this point in the history
Add user agent string to all client requests
  • Loading branch information
nikhiljindal committed Feb 4, 2015
2 parents 9ec61ce + 449f9d9 commit 63c07ad
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 196 deletions.
16 changes: 6 additions & 10 deletions hack/lib/etcd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
kube::etcd::start() {
local host=${ETCD_HOST:-127.0.0.1}
local port=${ETCD_PORT:-4001}
local testhost=${ETCD_PUBLIC_HOST:-localhost}

which etcd >/dev/null || {
kube::log::usage "etcd must be in your PATH"
Expand All @@ -38,18 +39,13 @@ kube::etcd::start() {

# Start etcd
ETCD_DIR=$(mktemp -d -t test-etcd.XXXXXX)
kube::log::usage "etcd -data-dir ${ETCD_DIR} -addr ${host}:${port} >/dev/null 2>/dev/null"
kube::log::usage "etcd -data-dir ${ETCD_DIR} --bind-addr ${host}:${port} >/dev/null 2>/dev/null"
etcd -data-dir ${ETCD_DIR} -addr ${host}:${port} >/dev/null 2>/dev/null &
ETCD_PID=$!

echo "Waiting for etcd to come up."
while true; do
if curl -L http://127.0.0.1:4001/v2/keys/test -XPUT -d value="test"; then
break
fi
done

kube::util::wait_for_url "http://${host}:${port}/v2/keys/test" "etcd: "

echo "Waiting for etcd to come up."
kube::util::wait_for_url "http://${testhost}:${port}/v2/machines" "etcd: " 0.25 80
curl -X PUT "http://${testhost}:${port}/v2/keys/_test"
}

kube::etcd::cleanup() {
Expand Down
2 changes: 1 addition & 1 deletion hack/local-up-cluster.sh
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ sudo "${GO_OUT}/kube-apiserver" \
--address="${API_HOST}" \
--port="${API_PORT}" \
--runtime_config=api/v1beta3 \
--etcd_servers="http://127.0.0.1:4001" \
--etcd_servers="http://localhost:4001" \
--portal_net="10.0.0.0/24" \
--cors_allowed_origins="${API_CORS_ALLOWED_ORIGINS}" >"${APISERVER_LOG}" 2>&1 &
APISERVER_PID=$!
Expand Down
26 changes: 26 additions & 0 deletions pkg/client/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import (
"io/ioutil"
"net/http"
"net/url"
"os"
"path"
"reflect"
gruntime "runtime"
"strings"

"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
Expand Down Expand Up @@ -68,6 +70,9 @@ type Config struct {
// certificate. For testing only.
Insecure bool

// UserAgent is an optional field that specifies the caller of this request.
UserAgent string

// Transport may be used for custom HTTP behavior. This attribute may not
// be specified with the TLS client certificate options.
Transport http.RoundTripper
Expand Down Expand Up @@ -151,6 +156,9 @@ func SetKubernetesDefaults(config *Config) error {
if config.Prefix == "" {
config.Prefix = "/api"
}
if len(config.UserAgent) == 0 {
config.UserAgent = DefaultKubernetesUserAgent()
}
if len(config.Version) == 0 {
config.Version = defaultVersionFor(config)
}
Expand Down Expand Up @@ -252,6 +260,9 @@ func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTrip
case hasBasicAuth:
rt = NewBasicAuthRoundTripper(config.Username, config.Password, rt)
}
if len(config.UserAgent) > 0 {
rt = NewUserAgentRoundTripper(config.UserAgent, rt)
}
return rt, nil
}

Expand Down Expand Up @@ -353,3 +364,18 @@ func defaultVersionFor(config *Config) string {
}
return version
}

// DefaultKubernetesUserAgent returns the default user agent that clients can use.
func DefaultKubernetesUserAgent() string {
commit := version.Get().GitCommit
if len(commit) > 7 {
commit = commit[:7]
}
if len(commit) == 0 {
commit = "unknown"
}
version := version.Get().GitVersion
seg := strings.SplitN(version, "-", 2)
version = seg[0]
return fmt.Sprintf("%s/%s (%s/%s) kubernetes/%s", path.Base(os.Args[0]), version, gruntime.GOOS, gruntime.GOARCH, commit)
}
58 changes: 58 additions & 0 deletions pkg/client/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ package client

import (
"net/http"
"reflect"
"strings"
"testing"

"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
)

const (
Expand Down Expand Up @@ -255,3 +259,57 @@ func TestIsConfigTransportTLS(t *testing.T) {
}
}
}

func TestSetKubernetesDefaults(t *testing.T) {
testCases := []struct {
Config Config
After Config
Err bool
}{
{
Config{},
Config{
Prefix: "/api",
Version: latest.Version,
Codec: latest.Codec,
LegacyBehavior: (latest.Version == "v1beta1" || latest.Version == "v1beta2"),
},
false,
},
{
Config{
Version: "not_an_api",
},
Config{},
true,
},
}
for _, testCase := range testCases {
val := &testCase.Config
err := SetKubernetesDefaults(val)
val.UserAgent = ""
switch {
case err == nil && testCase.Err:
t.Errorf("expected error but was nil")
continue
case err != nil && !testCase.Err:
t.Errorf("unexpected error %v", err)
continue
case err != nil:
continue
}
if !reflect.DeepEqual(*val, testCase.After) {
t.Errorf("unexpected result object: %#v", val)
}
}
}

func TestSetKubernetesDefaultsUserAgent(t *testing.T) {
config := &Config{}
if err := SetKubernetesDefaults(config); err != nil {
t.Errorf("unexpected error: %v")
}
if !strings.Contains(config.UserAgent, "kubernetes/") {
t.Errorf("no user agent set: %#v", config)
}
}
43 changes: 0 additions & 43 deletions pkg/client/restclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,49 +65,6 @@ func TestSetsCodec(t *testing.T) {
}
}

func TestSetDefaults(t *testing.T) {
testCases := []struct {
Config Config
After Config
Err bool
}{
{
Config{},
Config{
Prefix: "/api",
Version: latest.Version,
Codec: latest.Codec,
LegacyBehavior: (latest.Version == "v1beta1" || latest.Version == "v1beta2"),
},
false,
},
{
Config{
Version: "not_an_api",
},
Config{},
true,
},
}
for _, testCase := range testCases {
val := &testCase.Config
err := SetKubernetesDefaults(val)
switch {
case err == nil && testCase.Err:
t.Errorf("expected error but was nil")
continue
case err != nil && !testCase.Err:
t.Errorf("unexpected error %v", err)
continue
case err != nil:
continue
}
if !reflect.DeepEqual(*val, testCase.After) {
t.Errorf("unexpected result object: %#v", val)
}
}
}

func TestRESTClientRequires(t *testing.T) {
if _, err := RESTClientFor(&Config{Host: "127.0.0.1", Version: "", Codec: testapi.Codec()}); err == nil {
t.Errorf("unexpected non-error")
Expand Down
18 changes: 18 additions & 0 deletions pkg/client/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@ import (
"net/http"
)

type userAgentRoundTripper struct {
agent string
rt http.RoundTripper
}

func NewUserAgentRoundTripper(agent string, rt http.RoundTripper) http.RoundTripper {
return &userAgentRoundTripper{agent, rt}
}

func (rt *userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
if len(req.Header.Get("User-Agent")) != 0 {
return rt.rt.RoundTrip(req)
}
req = cloneRequest(req)
req.Header.Set("User-Agent", rt.agent)
return rt.rt.RoundTrip(req)
}

type basicAuthRoundTripper struct {
username string
password string
Expand Down
30 changes: 30 additions & 0 deletions pkg/client/transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,33 @@ func TestBasicAuthRoundTripper(t *testing.T) {
t.Errorf("unexpected authorization header: %#v", rt.Request)
}
}

func TestUserAgentRoundTripper(t *testing.T) {
rt := &testRoundTripper{}
req := &http.Request{
Header: make(http.Header),
}
req.Header.Set("User-Agent", "other")
NewUserAgentRoundTripper("test", rt).RoundTrip(req)
if rt.Request == nil {
t.Fatalf("unexpected nil request: %v", rt)
}
if rt.Request != req {
t.Fatalf("round tripper should not have copied request object: %#v", rt.Request)
}
if rt.Request.Header.Get("User-Agent") != "other" {
t.Errorf("unexpected user agent header: %#v", rt.Request)
}

req = &http.Request{}
NewUserAgentRoundTripper("test", rt).RoundTrip(req)
if rt.Request == nil {
t.Fatalf("unexpected nil request: %v", rt)
}
if rt.Request == req {
t.Fatalf("round tripper should have copied request object: %#v", rt.Request)
}
if rt.Request.Header.Get("User-Agent") != "test" {
t.Errorf("unexpected user agent header: %#v", rt.Request)
}
}
58 changes: 0 additions & 58 deletions pkg/kubectl/kubectl.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,79 +18,21 @@ limitations under the License.
package kubectl

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"reflect"
"strings"

"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
)

var apiVersionToUse = "v1beta1"

const kubectlAnnotationPrefix = "kubectl.kubernetes.io/"

func GetKubeClient(config *client.Config, matchVersion bool) (*client.Client, error) {
// TODO: get the namespace context when kubectl ns is completed
c, err := client.New(config)
if err != nil {
return nil, err
}

if matchVersion {
clientVersion := version.Get()
serverVersion, err := c.ServerVersion()
if err != nil {
return nil, fmt.Errorf("couldn't read version from server: %v\n", err)
}
if s := *serverVersion; !reflect.DeepEqual(clientVersion, s) {
return nil, fmt.Errorf("server version (%#v) differs from client version (%#v)!\n", s, clientVersion)
}
}

return c, nil
}

type NamespaceInfo struct {
Namespace string
}

// LoadNamespaceInfo parses a NamespaceInfo object from a file path. It creates a file at the specified path if it doesn't exist with the default namespace.
func LoadNamespaceInfo(path string) (*NamespaceInfo, error) {
var ns NamespaceInfo
if _, err := os.Stat(path); os.IsNotExist(err) {
ns.Namespace = api.NamespaceDefault
err = SaveNamespaceInfo(path, &ns)
return &ns, err
}
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
err = json.Unmarshal(data, &ns)
if err != nil {
return nil, err
}
return &ns, err
}

// SaveNamespaceInfo saves a NamespaceInfo object at the specified file path.
func SaveNamespaceInfo(path string, ns *NamespaceInfo) error {
if !util.IsDNSLabel(ns.Namespace) {
return fmt.Errorf("namespace %s is not a valid DNS Label", ns.Namespace)
}
data, err := json.Marshal(ns)
err = ioutil.WriteFile(path, data, 0600)
return err
}

// TODO Move to labels package.
func formatLabels(labelMap map[string]string) string {
l := labels.Set(labelMap).String()
Expand Down
Loading

0 comments on commit 63c07ad

Please sign in to comment.