From 2243999992ae6a6766e9329adffc509f46b1d46c Mon Sep 17 00:00:00 2001 From: Pete Moore Date: Fri, 21 Sep 2018 20:59:34 +0200 Subject: [PATCH 1/2] Bug 1428422 - add ROOTURL to Credentials struct --- codegenerator/model/api.go | 28 ++++----- creds.go | 28 ++++----- http.go | 50 ++++++---------- http_test.go | 85 +++++++--------------------- tcauth/auth_examples_test.go | 16 ++---- tcauth/tcauth.go | 25 ++++---- tcawsprovisioner/tcawsprovisioner.go | 35 +++--------- tcec2manager/tcec2manager.go | 35 +++--------- tcgithub/tcgithub.go | 25 ++++---- tchooks/tchooks.go | 25 ++++---- tcindex/tcindex.go | 25 ++++---- tclogin/tclogin.go | 25 ++++---- tcnotify/tcnotify.go | 25 ++++---- tcpurgecache/tcpurgecache.go | 25 ++++---- tcqueue/tcqueue.go | 25 ++++---- tcsecrets/tcsecrets.go | 25 ++++---- 16 files changed, 192 insertions(+), 310 deletions(-) diff --git a/codegenerator/model/api.go b/codegenerator/model/api.go index 64cbbb3..63e1c75 100644 --- a/codegenerator/model/api.go +++ b/codegenerator/model/api.go @@ -123,10 +123,6 @@ import ( tcclient "github.com/taskcluster/taskcluster-client-go" ) -const ( - DefaultBaseURL = "` + api.BaseURL + `" -) - type ` + api.apiDef.Name + ` tcclient.Client // New returns ` + text.IndefiniteArticle(api.apiDef.Name) + ` ` + api.apiDef.Name + ` client, configured to run against production. Pass in @@ -136,10 +132,7 @@ type ` + api.apiDef.Name + ` tcclient.Client ` // Here we want to add spaces between commands and comments, such that the comments line up, e.g.: // - // myQueue, credsError := queue.New(nil) // credentials loaded from TASKCLUSTER_* environment variables - // if credsError != nil { - // // handle malformed credentials... - // } + // myQueue := queue.New(nil) // credentials loaded from TASKCLUSTER_* environment variables // myQueue.Authenticate = false // disable authentication (creds above are now ignored) // myQueue.BaseURL = "http://localhost:1234/api/Queue/v1" // alternative API endpoint (production by default) // data, err := myQueue.Task(.....) // for example, call the Task(.....) API endpoint (described further down)... @@ -177,14 +170,14 @@ type ` + api.apiDef.Name + ` tcclient.Client } content += "// if err != nil {\n" - content += "// // handle errors...\n" + content += "// // handle errors...\n" content += "// }" content += ` func New(credentials *tcclient.Credentials) *` + api.apiDef.Name + ` { return &` + api.apiDef.Name + `{ Credentials: credentials, - BaseURL: DefaultBaseURL, - Authenticate: credentials != nil, + Service: "` + api.ServiceName + `", + Version: "` + "v1" + `", } } @@ -193,15 +186,18 @@ func New(credentials *tcclient.Credentials) *` + api.apiDef.Name + ` { // TASKCLUSTER_CLIENT_ID // TASKCLUSTER_ACCESS_TOKEN // TASKCLUSTER_CERTIFICATE +// TASKCLUSTER_ROOT_URL +// +// If environment variable TASKCLUSTER_ROOT_URL is empty string or not set, +// https://taskcluster.net will be assumed. // -// If environment variables TASKCLUSTER_CLIENT_ID is empty string or undefined +// If environment variable TASKCLUSTER_CLIENT_ID is empty string or not set, // authentication will be disabled. func NewFromEnv() *` + api.apiDef.Name + ` { - c := tcclient.CredentialsFromEnvVars() return &` + api.apiDef.Name + `{ - Credentials: c, - BaseURL: DefaultBaseURL, - Authenticate: c.ClientID != "", + Credentials: tcclient.CredentialsFromEnvVars(), + Service: "` + api.ServiceName + `", + Version: "` + "v1" + `", } } diff --git a/creds.go b/creds.go index ef01523..30262f2 100644 --- a/creds.go +++ b/creds.go @@ -1,7 +1,6 @@ package tcclient import ( - "context" "crypto/hmac" "crypto/sha256" "encoding/base64" @@ -34,6 +33,10 @@ type Credentials struct { // no scopes at all. // See https://docs.taskcluster.net/manual/apis/authorized-scopes AuthorizedScopes []string `json:"authorizedScopes"` + // RootURL is the root url of the taskcluster cluster to which the + // credentials pertain, for example "https://taskcluster.net" or + // "https://my.taskcluster.org:23430". + RootURL string } func (creds *Credentials) String() string { @@ -46,21 +49,19 @@ func (creds *Credentials) String() string { ) } -// Client is the entry point into all the functionality in this package. It -// contains authentication credentials, and a service endpoint, which are -// required for all HTTP operations. +// Client provides access to all taskcluster HTTP APIs for a given service, on +// a given API version, of a given deployed cluster. type Client struct { + // Version of client API, e.g. "v1" + Version string + // Service name, e.g. "aws-provisioner" + Service string + // Credentials (including root url of cluster credentials pertain to) for + // making API calls Credentials *Credentials - // The URL of the API endpoint to hit. - // For example, "https://auth.taskcluster.net/v1" for production auth service. - BaseURL string - // Whether authentication is enabled (e.g. set to 'false' when using taskcluster-proxy) - Authenticate bool - // HTTPClient is a ReducedHTTPClient to be used for the http call instead of - // the DefaultHTTPClient. + // HTTPClient is a ReducedHTTPClient to be used for http calls. If nil, + // DefaultHTTPClient will be used. HTTPClient ReducedHTTPClient - // Context that aborts all requests with this client - Context context.Context } // Certificate represents the certificate used in Temporary Credentials. See @@ -213,5 +214,6 @@ func CredentialsFromEnvVars() *Credentials { ClientID: os.Getenv("TASKCLUSTER_CLIENT_ID"), AccessToken: os.Getenv("TASKCLUSTER_ACCESS_TOKEN"), Certificate: os.Getenv("TASKCLUSTER_CERTIFICATE"), + RootURL: os.Getenv("TASKCLUSTER_ROOT_URL"), } } diff --git a/http.go b/http.go index bb4a961..6f2c524 100644 --- a/http.go +++ b/http.go @@ -9,17 +9,19 @@ import ( "io" "io/ioutil" "net/http" - "strings" - - // "net/http/httputil" "net/url" "reflect" "time" "github.com/taskcluster/httpbackoff" + tcurls "github.com/taskcluster/taskcluster-lib-urls" hawk "github.com/tent/hawk-go" ) +const ( + DefaultRootURL = "https://taskcluster.net" +) + // CallSummary provides information about the underlying http request and // response issued for a given API call. type CallSummary struct { @@ -81,19 +83,15 @@ type ReducedHTTPClient interface { // making multiple requests in various goroutines. var defaultHTTPClient ReducedHTTPClient = &http.Client{} -// utility function to create a URL object based on given data -func setURL(client *Client, route string, query url.Values) (u *url.URL, err error) { - URL := client.BaseURL - // See https://bugzil.la/1484702 - // Avoid double separator; routes must start with `/`, so baseURL shouldn't - // end with `/`. - if strings.HasSuffix(URL, "/") { - URL = URL[:len(URL)-1] +func (client *Client) UnsignedURL(route string, query url.Values) (u *url.URL, err error) { + rootURL := DefaultRootURL + if client.Credentials != nil && client.Credentials.RootURL != "" { + rootURL = client.Credentials.RootURL } - URL += route + URL := tcurls.API(rootURL, client.Service, client.Version, route) u, err = url.Parse(URL) if err != nil { - return nil, fmt.Errorf("Cannot parse url: '%v', is BaseURL (%v) set correctly?\n%v\n", URL, client.BaseURL, err) + return nil, fmt.Errorf("Cannot parse url: '%v', is RootURL (%v) correct?\n%v\n", URL, client.Credentials.RootURL, err) } if query != nil { u.RawQuery = query.Encode() @@ -115,39 +113,31 @@ func (client *Client) Request(rawPayload []byte, method, route string, query url httpCall := func() (*http.Response, error, error) { var ioReader io.Reader ioReader = bytes.NewReader(rawPayload) - u, err := setURL(client, route, query) + u, err := client.UnsignedURL(route, query) if err != nil { return nil, nil, fmt.Errorf("apiCall url cannot be parsed:\n%v\n", err) } callSummary.HTTPRequest, err = http.NewRequest(method, u.String(), ioReader) if err != nil { - return nil, nil, fmt.Errorf("Internal error: apiCall url cannot be parsed although thought to be valid: '%v', is the BaseURL (%v) set correctly?\n%v\n", u.String(), client.BaseURL, err) + return nil, nil, fmt.Errorf("Internal error: apiCall url cannot be parsed although thought to be valid: '%v', is the RootURL (%v) correct?\n%v\n", u.String(), client.Credentials.RootURL, err) } if len(rawPayload) > 0 { callSummary.HTTPRequest.Header.Set("Content-Type", "application/json") } // Refresh Authorization header with each call... // Only authenticate if client library user wishes to. - if client.Authenticate { + if client.Credentials != nil && client.Credentials.ClientID != "" { err = client.Credentials.SignRequest(callSummary.HTTPRequest) if err != nil { return nil, nil, err } } - // Set context if one is given - if client.Context != nil { - callSummary.HTTPRequest = callSummary.HTTPRequest.WithContext(client.Context) - } var resp *http.Response if client.HTTPClient != nil { resp, err = client.HTTPClient.Do(callSummary.HTTPRequest) } else { resp, err = defaultHTTPClient.Do(callSummary.HTTPRequest) } - // return cancelled error, if context was cancelled - if client.Context != nil && client.Context.Err() != nil { - return nil, nil, client.Context.Err() - } // b, e := httputil.DumpResponse(resp, true) // if e == nil { // fmt.Println(string(b)) @@ -183,7 +173,7 @@ func (c *Credentials) SignRequest(req *http.Request) (err error) { Hash: sha256.New, } reqAuth := hawk.NewRequestAuth(req, credentials, 0) - reqAuth.Ext, err = getExtHeader(c) + reqAuth.Ext, err = c.ExtHeader() if err != nil { return fmt.Errorf("Internal error: was not able to generate hawk ext header from provided credentials:\n%s\n%s", c, err) } @@ -223,10 +213,6 @@ func (client *Client) APICall(payload interface{}, method, route string, result callSummary, err := client.Request(rawPayload, method, route, query) callSummary.HTTPRequestObject = payload if err != nil { - // If context failed during this request, then we should just return that error - if client.Context != nil && client.Context.Err() != nil { - return result, callSummary, client.Context.Err() - } return result, callSummary, &APICallException{ @@ -256,7 +242,7 @@ func (client *Client) APICall(payload interface{}, method, route string, result // query string parameters, if any, and duration is the amount of time that the // signed URL should remain valid for. func (client *Client) SignedURL(route string, query url.Values, duration time.Duration) (u *url.URL, err error) { - u, err = setURL(client, route, query) + u, err = client.UnsignedURL(route, query) if err != nil { return } @@ -269,7 +255,7 @@ func (client *Client) SignedURL(route string, query url.Values, duration time.Du if err != nil { return } - reqAuth.Ext, err = getExtHeader(client.Credentials) + reqAuth.Ext, err = client.Credentials.ExtHeader() if err != nil { return } @@ -295,7 +281,7 @@ func (client *Client) SignedURL(route string, query url.Values, duration time.Du // See: // * https://docs.taskcluster.net/manual/apis/authorized-scopes // * https://docs.taskcluster.net/manual/apis/temporary-credentials -func getExtHeader(credentials *Credentials) (header string, err error) { +func (credentials *Credentials) ExtHeader() (header string, err error) { ext := &ExtHeader{} if credentials.Certificate != "" { certObj := new(Certificate) diff --git a/http_test.go b/http_test.go index 15ee334..052a1d7 100644 --- a/http_test.go +++ b/http_test.go @@ -2,7 +2,6 @@ package tcclient import ( "bytes" - "context" "encoding/base64" "encoding/json" "io/ioutil" @@ -123,7 +122,7 @@ func checkExtHeaderTempCreds(t *testing.T, permCreds *Credentials) { if err != nil { t.Fatalf("Received error when generating temporary credentials: %s", err) } - actualHeader, err := getExtHeader(tempCredentials) + actualHeader, err := tempCredentials.ExtHeader() if err != nil { t.Fatalf("Received error when generating ext header: %s", err) } @@ -166,7 +165,7 @@ func checkExtHeaderTempCreds(t *testing.T, permCreds *Credentials) { // checkExtHeader simply checks if getExtHeader returns the same results as the // specified expected header. func checkExtHeader(t *testing.T, creds *Credentials, expectedHeader string) { - actualHeader, err := getExtHeader(creds) + actualHeader, err := creds.ExtHeader() if err != nil { t.Fatalf("Received error when generating ext header: %s", err) } @@ -175,41 +174,6 @@ func checkExtHeader(t *testing.T, creds *Credentials, expectedHeader string) { } } -func TestRequestWithContext(t *testing.T) { - s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - time.Sleep(2 * time.Second) - w.WriteHeader(200) - w.Write([]byte(`{"value": "hello world"}`)) - })) - defer s.Close() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - c := Client{ - BaseURL: s.URL, - Authenticate: false, - Context: ctx, - } - - // Make a call - var result struct { - Value string `json:"value"` - } - _, _, err := c.APICall(nil, "GET", "/whatever", &result, nil) - if err != nil { - t.Fatal("Unexpected error: ", err) - } - - // Make a call and cancel - time.AfterFunc(100*time.Millisecond, cancel) - _, _, err = c.APICall(nil, "GET", "/whatever", &result, nil) - if err == nil { - t.Fatal("Should have had a cancel error") - } - if err != context.Canceled { - t.Fatalf("Expected canceled error but got %T %v", err, err) - } -} - // Make sure Content-Type is only set if there is a payload func TestContentTypeHeader(t *testing.T) { // This mock service just returns the value of the Content-Type request @@ -220,8 +184,9 @@ func TestContentTypeHeader(t *testing.T) { })) defer s.Close() client := Client{ - BaseURL: s.URL, - Authenticate: false, + Credentials: &Credentials{ + RootURL: s.URL, + }, } // Three following calls should have no Content-Header set since request body is empty @@ -307,7 +272,7 @@ func (m *MockHTTPClient) Requests() []MockHTTPRequest { } type RequestTestCase struct { - BaseURL string + RootURL string RequestBody []byte Method string Route string @@ -321,14 +286,14 @@ func TestHTTPRequestGeneration(t *testing.T) { // configured by user, so we should test with both trailing and // non-trailing slash; see https://bugzil.la/1484702 { - BaseURL: "https://queue.taskcluster.net/v1", + RootURL: "https://taskcluster.net", RequestBody: nil, Method: "GET", Route: "/a/b", QueryParameters: nil, }, { - BaseURL: "https://my.taskcluster.queue.deployment/v1/", + RootURL: "https://my.taskcluster.deployment", RequestBody: nil, Method: "GET", Route: "/a/b", @@ -336,53 +301,41 @@ func TestHTTPRequestGeneration(t *testing.T) { }, // test a request with a payload body and query string parameters { - BaseURL: "https://my.taskcluster.queue.deployment/v1/", + RootURL: "https://my.taskcluster.deployment", RequestBody: []byte{1, 2, 3, 4, 5}, Method: "POST", Route: "/a/b", QueryParameters: url.Values{"a": []string{"A", "B"}}, }, - // fully qualified routes with empty base urls are used by - // taskcluster-proxy - { - BaseURL: "", - RequestBody: nil, - Method: "GET", - Route: "https://localhost:12345/a/b", - QueryParameters: nil, - }, } expectedRequests := []MockHTTPRequest{ { URL: "https://queue.taskcluster.net/v1/a/b", Method: "GET", - Body: []byte{}, + Body: []uint8{}, }, { - URL: "https://my.taskcluster.queue.deployment/v1/a/b", + URL: "https://my.taskcluster.deployment/api/queue/v1/a/b", Method: "GET", - Body: []byte{}, + Body: []uint8{}, }, { - URL: "https://my.taskcluster.queue.deployment/v1/a/b?a=A&a=B", + URL: "https://my.taskcluster.deployment/api/queue/v1/a/b?a=A&a=B", Method: "POST", - Body: []byte{1, 2, 3, 4, 5}, - }, - { - URL: "https://localhost:12345/a/b", - Method: "GET", - Body: []byte{}, + Body: []uint8{0x1, 0x2, 0x3, 0x4, 0x5}, }, } mockHTTPClient := &MockHTTPClient{T: t} c := Client{ - Authenticate: false, - HTTPClient: mockHTTPClient, + HTTPClient: mockHTTPClient, + Credentials: &Credentials{}, + Version: "v1", + Service: "queue", } for _, testCase := range testCases { - c.BaseURL = testCase.BaseURL + c.Credentials.RootURL = testCase.RootURL c.Request(testCase.RequestBody, testCase.Method, testCase.Route, testCase.QueryParameters) } actualRequests := mockHTTPClient.Requests() diff --git a/tcauth/auth_examples_test.go b/tcauth/auth_examples_test.go index 430f635..a70f8e9 100644 --- a/tcauth/auth_examples_test.go +++ b/tcauth/auth_examples_test.go @@ -14,12 +14,7 @@ func Example_scopes() { // Note: the API call we will make doesn't need credentials as it supplies public information. // However, for the purpose of demonstrating the general case, this is how you can provide // credentials for API calls that require them. - myAuth := tcauth.New( - &tcclient.Credentials{ - ClientID: "SOME-CLIENT-ID", - AccessToken: "SOME-WELL-FORMED-ACCESS-TOKEN", - }, - ) + myAuth := tcauth.NewFromEnv() // Look up client details for client id "project/taskcluster/tc-client-go/tests"... resp, err := myAuth.Client("project/taskcluster/tc-client-go/tests") @@ -46,10 +41,11 @@ func Example_updateClient() { // In this example we will connect to a local auth server running on // localhost with authentication disabled. This would also work for // connecting to a local taskcluster-proxy instance. - myAuth := tcauth.New(nil) - - // Set target url to localhost url... - myAuth.BaseURL = "http://localhost:60024/v1" + myAuth := tcauth.New( + &tcclient.Credentials{ + RootURL: "http://localhost:60024", + }, + ) // Update client id "b2g-power-tests" with new description and expiry... client, err := myAuth.UpdateClient( diff --git a/tcauth/tcauth.go b/tcauth/tcauth.go index fb5ab57..04c129c 100644 --- a/tcauth/tcauth.go +++ b/tcauth/tcauth.go @@ -81,10 +81,6 @@ import ( tcclient "github.com/taskcluster/taskcluster-client-go" ) -const ( - DefaultBaseURL = "https://auth.taskcluster.net/v1/" -) - type Auth tcclient.Client // New returns an Auth client, configured to run against production. Pass in @@ -95,13 +91,13 @@ type Auth tcclient.Client // auth.BaseURL = "http://localhost:1234/api/Auth/v1" // alternative API endpoint (production by default) // err := auth.Ping(.....) // for example, call the Ping(.....) API endpoint (described further down)... // if err != nil { -// // handle errors... +// // handle errors... // } func New(credentials *tcclient.Credentials) *Auth { return &Auth{ - Credentials: credentials, - BaseURL: DefaultBaseURL, - Authenticate: credentials != nil, + Credentials: credentials, + Service: "auth", + Version: "v1", } } @@ -110,15 +106,18 @@ func New(credentials *tcclient.Credentials) *Auth { // TASKCLUSTER_CLIENT_ID // TASKCLUSTER_ACCESS_TOKEN // TASKCLUSTER_CERTIFICATE +// TASKCLUSTER_ROOT_URL +// +// If environment variable TASKCLUSTER_ROOT_URL is empty string or not set, +// https://taskcluster.net will be assumed. // -// If environment variables TASKCLUSTER_CLIENT_ID is empty string or undefined +// If environment variable TASKCLUSTER_CLIENT_ID is empty string or not set, // authentication will be disabled. func NewFromEnv() *Auth { - c := tcclient.CredentialsFromEnvVars() return &Auth{ - Credentials: c, - BaseURL: DefaultBaseURL, - Authenticate: c.ClientID != "", + Credentials: tcclient.CredentialsFromEnvVars(), + Service: "auth", + Version: "v1", } } diff --git a/tcawsprovisioner/tcawsprovisioner.go b/tcawsprovisioner/tcawsprovisioner.go index 4f084f4..dee09fc 100644 --- a/tcawsprovisioner/tcawsprovisioner.go +++ b/tcawsprovisioner/tcawsprovisioner.go @@ -69,44 +69,23 @@ import ( tcclient "github.com/taskcluster/taskcluster-client-go" ) -const ( - DefaultBaseURL = "https://aws-provisioner.taskcluster.net/v1" -) - type AwsProvisioner tcclient.Client -// New returns an AwsProvisioner client, configured to run against production. Pass in -// nil to create a client without authentication. The -// returned client is mutable, so returned settings can be altered. -// -// awsProvisioner := tcawsprovisioner.New(nil) // client without authentication -// awsProvisioner.BaseURL = "http://localhost:1234/api/AwsProvisioner/v1" // alternative API endpoint (production by default) -// data, err := awsProvisioner.ListWorkerTypeSummaries(.....) // for example, call the ListWorkerTypeSummaries(.....) API endpoint (described further down)... -// if err != nil { -// // handle errors... -// } -func New(credentials *tcclient.Credentials) *AwsProvisioner { - return &AwsProvisioner{ - Credentials: credentials, - BaseURL: DefaultBaseURL, - Authenticate: credentials != nil, - } -} - -// NewFromEnv returns an AwsProvisioner client with credentials taken from the environment variables: +// NewFromEnv returns an Auth client with credentials taken from the environment variables: // // TASKCLUSTER_CLIENT_ID // TASKCLUSTER_ACCESS_TOKEN // TASKCLUSTER_CERTIFICATE +// TASKCLUSTER_ROOT_URL +// +// If environment variable TASKCLUSTER_ROOT_URL is empty string or not set, +// https://taskcluster.net will be assumed. // -// If environment variables TASKCLUSTER_CLIENT_ID is empty string or undefined +// If environment variable TASKCLUSTER_CLIENT_ID is empty string or not set, // authentication will be disabled. func NewFromEnv() *AwsProvisioner { - c := tcclient.CredentialsFromEnvVars() return &AwsProvisioner{ - Credentials: c, - BaseURL: DefaultBaseURL, - Authenticate: c.ClientID != "", + Credentials: tcclient.CredentialsFromEnvVars(), } } diff --git a/tcec2manager/tcec2manager.go b/tcec2manager/tcec2manager.go index 5af57bb..fec6b07 100644 --- a/tcec2manager/tcec2manager.go +++ b/tcec2manager/tcec2manager.go @@ -43,44 +43,23 @@ import ( tcclient "github.com/taskcluster/taskcluster-client-go" ) -const ( - DefaultBaseURL = "localhost:5555/v1" -) - type EC2Manager tcclient.Client -// New returns an EC2Manager client, configured to run against production. Pass in -// nil to create a client without authentication. The -// returned client is mutable, so returned settings can be altered. -// -// eC2Manager := tcec2manager.New(nil) // client without authentication -// eC2Manager.BaseURL = "http://localhost:1234/api/EC2Manager/v1" // alternative API endpoint (production by default) -// data, err := eC2Manager.ListWorkerTypes(.....) // for example, call the ListWorkerTypes(.....) API endpoint (described further down)... -// if err != nil { -// // handle errors... -// } -func New(credentials *tcclient.Credentials) *EC2Manager { - return &EC2Manager{ - Credentials: credentials, - BaseURL: DefaultBaseURL, - Authenticate: credentials != nil, - } -} - -// NewFromEnv returns an EC2Manager client with credentials taken from the environment variables: +// NewFromEnv returns an Auth client with credentials taken from the environment variables: // // TASKCLUSTER_CLIENT_ID // TASKCLUSTER_ACCESS_TOKEN // TASKCLUSTER_CERTIFICATE +// TASKCLUSTER_ROOT_URL +// +// If environment variable TASKCLUSTER_ROOT_URL is empty string or not set, +// https://taskcluster.net will be assumed. // -// If environment variables TASKCLUSTER_CLIENT_ID is empty string or undefined +// If environment variable TASKCLUSTER_CLIENT_ID is empty string or not set, // authentication will be disabled. func NewFromEnv() *EC2Manager { - c := tcclient.CredentialsFromEnvVars() return &EC2Manager{ - Credentials: c, - BaseURL: DefaultBaseURL, - Authenticate: c.ClientID != "", + Credentials: tcclient.CredentialsFromEnvVars(), } } diff --git a/tcgithub/tcgithub.go b/tcgithub/tcgithub.go index af8e9b9..f5b8e39 100644 --- a/tcgithub/tcgithub.go +++ b/tcgithub/tcgithub.go @@ -50,10 +50,6 @@ import ( tcclient "github.com/taskcluster/taskcluster-client-go" ) -const ( - DefaultBaseURL = "https://github.taskcluster.net/v1/" -) - type Github tcclient.Client // New returns a Github client, configured to run against production. Pass in @@ -64,13 +60,13 @@ type Github tcclient.Client // github.BaseURL = "http://localhost:1234/api/Github/v1" // alternative API endpoint (production by default) // err := github.Ping(.....) // for example, call the Ping(.....) API endpoint (described further down)... // if err != nil { -// // handle errors... +// // handle errors... // } func New(credentials *tcclient.Credentials) *Github { return &Github{ - Credentials: credentials, - BaseURL: DefaultBaseURL, - Authenticate: credentials != nil, + Credentials: credentials, + Service: "github", + Version: "v1", } } @@ -79,15 +75,18 @@ func New(credentials *tcclient.Credentials) *Github { // TASKCLUSTER_CLIENT_ID // TASKCLUSTER_ACCESS_TOKEN // TASKCLUSTER_CERTIFICATE +// TASKCLUSTER_ROOT_URL +// +// If environment variable TASKCLUSTER_ROOT_URL is empty string or not set, +// https://taskcluster.net will be assumed. // -// If environment variables TASKCLUSTER_CLIENT_ID is empty string or undefined +// If environment variable TASKCLUSTER_CLIENT_ID is empty string or not set, // authentication will be disabled. func NewFromEnv() *Github { - c := tcclient.CredentialsFromEnvVars() return &Github{ - Credentials: c, - BaseURL: DefaultBaseURL, - Authenticate: c.ClientID != "", + Credentials: tcclient.CredentialsFromEnvVars(), + Service: "github", + Version: "v1", } } diff --git a/tchooks/tchooks.go b/tchooks/tchooks.go index 52f3433..6f43814 100644 --- a/tchooks/tchooks.go +++ b/tchooks/tchooks.go @@ -61,10 +61,6 @@ import ( tcclient "github.com/taskcluster/taskcluster-client-go" ) -const ( - DefaultBaseURL = "https://hooks.taskcluster.net/v1/" -) - type Hooks tcclient.Client // New returns a Hooks client, configured to run against production. Pass in @@ -75,13 +71,13 @@ type Hooks tcclient.Client // hooks.BaseURL = "http://localhost:1234/api/Hooks/v1" // alternative API endpoint (production by default) // err := hooks.Ping(.....) // for example, call the Ping(.....) API endpoint (described further down)... // if err != nil { -// // handle errors... +// // handle errors... // } func New(credentials *tcclient.Credentials) *Hooks { return &Hooks{ - Credentials: credentials, - BaseURL: DefaultBaseURL, - Authenticate: credentials != nil, + Credentials: credentials, + Service: "hooks", + Version: "v1", } } @@ -90,15 +86,18 @@ func New(credentials *tcclient.Credentials) *Hooks { // TASKCLUSTER_CLIENT_ID // TASKCLUSTER_ACCESS_TOKEN // TASKCLUSTER_CERTIFICATE +// TASKCLUSTER_ROOT_URL +// +// If environment variable TASKCLUSTER_ROOT_URL is empty string or not set, +// https://taskcluster.net will be assumed. // -// If environment variables TASKCLUSTER_CLIENT_ID is empty string or undefined +// If environment variable TASKCLUSTER_CLIENT_ID is empty string or not set, // authentication will be disabled. func NewFromEnv() *Hooks { - c := tcclient.CredentialsFromEnvVars() return &Hooks{ - Credentials: c, - BaseURL: DefaultBaseURL, - Authenticate: c.ClientID != "", + Credentials: tcclient.CredentialsFromEnvVars(), + Service: "hooks", + Version: "v1", } } diff --git a/tcindex/tcindex.go b/tcindex/tcindex.go index 3101873..2d5c1f2 100644 --- a/tcindex/tcindex.go +++ b/tcindex/tcindex.go @@ -134,10 +134,6 @@ import ( tcclient "github.com/taskcluster/taskcluster-client-go" ) -const ( - DefaultBaseURL = "https://index.taskcluster.net/v1/" -) - type Index tcclient.Client // New returns an Index client, configured to run against production. Pass in @@ -148,13 +144,13 @@ type Index tcclient.Client // index.BaseURL = "http://localhost:1234/api/Index/v1" // alternative API endpoint (production by default) // err := index.Ping(.....) // for example, call the Ping(.....) API endpoint (described further down)... // if err != nil { -// // handle errors... +// // handle errors... // } func New(credentials *tcclient.Credentials) *Index { return &Index{ - Credentials: credentials, - BaseURL: DefaultBaseURL, - Authenticate: credentials != nil, + Credentials: credentials, + Service: "index", + Version: "v1", } } @@ -163,15 +159,18 @@ func New(credentials *tcclient.Credentials) *Index { // TASKCLUSTER_CLIENT_ID // TASKCLUSTER_ACCESS_TOKEN // TASKCLUSTER_CERTIFICATE +// TASKCLUSTER_ROOT_URL +// +// If environment variable TASKCLUSTER_ROOT_URL is empty string or not set, +// https://taskcluster.net will be assumed. // -// If environment variables TASKCLUSTER_CLIENT_ID is empty string or undefined +// If environment variable TASKCLUSTER_CLIENT_ID is empty string or not set, // authentication will be disabled. func NewFromEnv() *Index { - c := tcclient.CredentialsFromEnvVars() return &Index{ - Credentials: c, - BaseURL: DefaultBaseURL, - Authenticate: c.ClientID != "", + Credentials: tcclient.CredentialsFromEnvVars(), + Service: "index", + Version: "v1", } } diff --git a/tclogin/tclogin.go b/tclogin/tclogin.go index 426bb8a..fbf1f2e 100644 --- a/tclogin/tclogin.go +++ b/tclogin/tclogin.go @@ -43,10 +43,6 @@ import ( tcclient "github.com/taskcluster/taskcluster-client-go" ) -const ( - DefaultBaseURL = "https://login.taskcluster.net/v1" -) - type Login tcclient.Client // New returns a Login client, configured to run against production. Pass in @@ -57,13 +53,13 @@ type Login tcclient.Client // login.BaseURL = "http://localhost:1234/api/Login/v1" // alternative API endpoint (production by default) // err := login.Ping(.....) // for example, call the Ping(.....) API endpoint (described further down)... // if err != nil { -// // handle errors... +// // handle errors... // } func New(credentials *tcclient.Credentials) *Login { return &Login{ - Credentials: credentials, - BaseURL: DefaultBaseURL, - Authenticate: credentials != nil, + Credentials: credentials, + Service: "login", + Version: "v1", } } @@ -72,15 +68,18 @@ func New(credentials *tcclient.Credentials) *Login { // TASKCLUSTER_CLIENT_ID // TASKCLUSTER_ACCESS_TOKEN // TASKCLUSTER_CERTIFICATE +// TASKCLUSTER_ROOT_URL +// +// If environment variable TASKCLUSTER_ROOT_URL is empty string or not set, +// https://taskcluster.net will be assumed. // -// If environment variables TASKCLUSTER_CLIENT_ID is empty string or undefined +// If environment variable TASKCLUSTER_CLIENT_ID is empty string or not set, // authentication will be disabled. func NewFromEnv() *Login { - c := tcclient.CredentialsFromEnvVars() return &Login{ - Credentials: c, - BaseURL: DefaultBaseURL, - Authenticate: c.ClientID != "", + Credentials: tcclient.CredentialsFromEnvVars(), + Service: "login", + Version: "v1", } } diff --git a/tcnotify/tcnotify.go b/tcnotify/tcnotify.go index 32874c6..9f31869 100644 --- a/tcnotify/tcnotify.go +++ b/tcnotify/tcnotify.go @@ -42,10 +42,6 @@ import ( tcclient "github.com/taskcluster/taskcluster-client-go" ) -const ( - DefaultBaseURL = "https://notify.taskcluster.net/v1/" -) - type Notify tcclient.Client // New returns a Notify client, configured to run against production. Pass in @@ -56,13 +52,13 @@ type Notify tcclient.Client // notify.BaseURL = "http://localhost:1234/api/Notify/v1" // alternative API endpoint (production by default) // err := notify.Ping(.....) // for example, call the Ping(.....) API endpoint (described further down)... // if err != nil { -// // handle errors... +// // handle errors... // } func New(credentials *tcclient.Credentials) *Notify { return &Notify{ - Credentials: credentials, - BaseURL: DefaultBaseURL, - Authenticate: credentials != nil, + Credentials: credentials, + Service: "notify", + Version: "v1", } } @@ -71,15 +67,18 @@ func New(credentials *tcclient.Credentials) *Notify { // TASKCLUSTER_CLIENT_ID // TASKCLUSTER_ACCESS_TOKEN // TASKCLUSTER_CERTIFICATE +// TASKCLUSTER_ROOT_URL +// +// If environment variable TASKCLUSTER_ROOT_URL is empty string or not set, +// https://taskcluster.net will be assumed. // -// If environment variables TASKCLUSTER_CLIENT_ID is empty string or undefined +// If environment variable TASKCLUSTER_CLIENT_ID is empty string or not set, // authentication will be disabled. func NewFromEnv() *Notify { - c := tcclient.CredentialsFromEnvVars() return &Notify{ - Credentials: c, - BaseURL: DefaultBaseURL, - Authenticate: c.ClientID != "", + Credentials: tcclient.CredentialsFromEnvVars(), + Service: "notify", + Version: "v1", } } diff --git a/tcpurgecache/tcpurgecache.go b/tcpurgecache/tcpurgecache.go index e1d1218..d192a5f 100644 --- a/tcpurgecache/tcpurgecache.go +++ b/tcpurgecache/tcpurgecache.go @@ -47,10 +47,6 @@ import ( tcclient "github.com/taskcluster/taskcluster-client-go" ) -const ( - DefaultBaseURL = "https://purge-cache.taskcluster.net/v1/" -) - type PurgeCache tcclient.Client // New returns a PurgeCache client, configured to run against production. Pass in @@ -61,13 +57,13 @@ type PurgeCache tcclient.Client // purgeCache.BaseURL = "http://localhost:1234/api/PurgeCache/v1" // alternative API endpoint (production by default) // err := purgeCache.Ping(.....) // for example, call the Ping(.....) API endpoint (described further down)... // if err != nil { -// // handle errors... +// // handle errors... // } func New(credentials *tcclient.Credentials) *PurgeCache { return &PurgeCache{ - Credentials: credentials, - BaseURL: DefaultBaseURL, - Authenticate: credentials != nil, + Credentials: credentials, + Service: "purge-cache", + Version: "v1", } } @@ -76,15 +72,18 @@ func New(credentials *tcclient.Credentials) *PurgeCache { // TASKCLUSTER_CLIENT_ID // TASKCLUSTER_ACCESS_TOKEN // TASKCLUSTER_CERTIFICATE +// TASKCLUSTER_ROOT_URL +// +// If environment variable TASKCLUSTER_ROOT_URL is empty string or not set, +// https://taskcluster.net will be assumed. // -// If environment variables TASKCLUSTER_CLIENT_ID is empty string or undefined +// If environment variable TASKCLUSTER_CLIENT_ID is empty string or not set, // authentication will be disabled. func NewFromEnv() *PurgeCache { - c := tcclient.CredentialsFromEnvVars() return &PurgeCache{ - Credentials: c, - BaseURL: DefaultBaseURL, - Authenticate: c.ClientID != "", + Credentials: tcclient.CredentialsFromEnvVars(), + Service: "purge-cache", + Version: "v1", } } diff --git a/tcqueue/tcqueue.go b/tcqueue/tcqueue.go index c3d1008..3d7204d 100644 --- a/tcqueue/tcqueue.go +++ b/tcqueue/tcqueue.go @@ -51,10 +51,6 @@ import ( tcclient "github.com/taskcluster/taskcluster-client-go" ) -const ( - DefaultBaseURL = "https://queue.taskcluster.net/v1/" -) - type Queue tcclient.Client // New returns a Queue client, configured to run against production. Pass in @@ -65,13 +61,13 @@ type Queue tcclient.Client // queue.BaseURL = "http://localhost:1234/api/Queue/v1" // alternative API endpoint (production by default) // err := queue.Ping(.....) // for example, call the Ping(.....) API endpoint (described further down)... // if err != nil { -// // handle errors... +// // handle errors... // } func New(credentials *tcclient.Credentials) *Queue { return &Queue{ - Credentials: credentials, - BaseURL: DefaultBaseURL, - Authenticate: credentials != nil, + Credentials: credentials, + Service: "queue", + Version: "v1", } } @@ -80,15 +76,18 @@ func New(credentials *tcclient.Credentials) *Queue { // TASKCLUSTER_CLIENT_ID // TASKCLUSTER_ACCESS_TOKEN // TASKCLUSTER_CERTIFICATE +// TASKCLUSTER_ROOT_URL +// +// If environment variable TASKCLUSTER_ROOT_URL is empty string or not set, +// https://taskcluster.net will be assumed. // -// If environment variables TASKCLUSTER_CLIENT_ID is empty string or undefined +// If environment variable TASKCLUSTER_CLIENT_ID is empty string or not set, // authentication will be disabled. func NewFromEnv() *Queue { - c := tcclient.CredentialsFromEnvVars() return &Queue{ - Credentials: c, - BaseURL: DefaultBaseURL, - Authenticate: c.ClientID != "", + Credentials: tcclient.CredentialsFromEnvVars(), + Service: "queue", + Version: "v1", } } diff --git a/tcsecrets/tcsecrets.go b/tcsecrets/tcsecrets.go index 4b41bd5..5b508a2 100644 --- a/tcsecrets/tcsecrets.go +++ b/tcsecrets/tcsecrets.go @@ -49,10 +49,6 @@ import ( tcclient "github.com/taskcluster/taskcluster-client-go" ) -const ( - DefaultBaseURL = "https://secrets.taskcluster.net/v1/" -) - type Secrets tcclient.Client // New returns a Secrets client, configured to run against production. Pass in @@ -63,13 +59,13 @@ type Secrets tcclient.Client // secrets.BaseURL = "http://localhost:1234/api/Secrets/v1" // alternative API endpoint (production by default) // err := secrets.Ping(.....) // for example, call the Ping(.....) API endpoint (described further down)... // if err != nil { -// // handle errors... +// // handle errors... // } func New(credentials *tcclient.Credentials) *Secrets { return &Secrets{ - Credentials: credentials, - BaseURL: DefaultBaseURL, - Authenticate: credentials != nil, + Credentials: credentials, + Service: "secrets", + Version: "v1", } } @@ -78,15 +74,18 @@ func New(credentials *tcclient.Credentials) *Secrets { // TASKCLUSTER_CLIENT_ID // TASKCLUSTER_ACCESS_TOKEN // TASKCLUSTER_CERTIFICATE +// TASKCLUSTER_ROOT_URL +// +// If environment variable TASKCLUSTER_ROOT_URL is empty string or not set, +// https://taskcluster.net will be assumed. // -// If environment variables TASKCLUSTER_CLIENT_ID is empty string or undefined +// If environment variable TASKCLUSTER_CLIENT_ID is empty string or not set, // authentication will be disabled. func NewFromEnv() *Secrets { - c := tcclient.CredentialsFromEnvVars() return &Secrets{ - Credentials: c, - BaseURL: DefaultBaseURL, - Authenticate: c.ClientID != "", + Credentials: tcclient.CredentialsFromEnvVars(), + Service: "secrets", + Version: "v1", } } From a9e04adab16d75aa770191121347161a3ba94e74 Mon Sep 17 00:00:00 2001 From: Pete Moore Date: Tue, 25 Sep 2018 17:52:29 +0200 Subject: [PATCH 2/2] wip --- codegenerator/model/api.go | 16 ++++++++++------ codegenerator/model/types.go | 13 ++++++++++--- creds.go | 6 ++++-- http.go | 2 +- http_test.go | 4 ++-- tcauth/tcauth.go | 12 ++++++------ tcgithub/tcgithub.go | 12 ++++++------ tchooks/tchooks.go | 12 ++++++------ tcindex/tcindex.go | 12 ++++++------ tclogin/tclogin.go | 12 ++++++------ tcnotify/tcnotify.go | 12 ++++++------ tcpurgecache/tcpurgecache.go | 12 ++++++------ tcqueue/tcqueue.go | 12 ++++++------ tcsecrets/tcsecrets.go | 12 ++++++------ 14 files changed, 81 insertions(+), 68 deletions(-) diff --git a/codegenerator/model/api.go b/codegenerator/model/api.go index 63e1c75..6ec5aef 100644 --- a/codegenerator/model/api.go +++ b/codegenerator/model/api.go @@ -172,12 +172,16 @@ type ` + api.apiDef.Name + ` tcclient.Client content += "// if err != nil {\n" content += "// // handle errors...\n" content += "// }" + apiVersion := api.APIVersion + if apiVersion == "" { + apiVersion = "v1" + } content += ` func New(credentials *tcclient.Credentials) *` + api.apiDef.Name + ` { return &` + api.apiDef.Name + `{ Credentials: credentials, - Service: "` + api.ServiceName + `", - Version: "` + "v1" + `", + ServiceName: "` + api.ServiceName + `", + APIVersion: "` + apiVersion + `", } } @@ -188,16 +192,16 @@ func New(credentials *tcclient.Credentials) *` + api.apiDef.Name + ` { // TASKCLUSTER_CERTIFICATE // TASKCLUSTER_ROOT_URL // -// If environment variable TASKCLUSTER_ROOT_URL is empty string or not set, -// https://taskcluster.net will be assumed. +// No validation is performed on the loaded values, and unset environment +// variables will result in empty string values. // // If environment variable TASKCLUSTER_CLIENT_ID is empty string or not set, // authentication will be disabled. func NewFromEnv() *` + api.apiDef.Name + ` { return &` + api.apiDef.Name + `{ Credentials: tcclient.CredentialsFromEnvVars(), - Service: "` + api.ServiceName + `", - Version: "` + "v1" + `", + ServiceName: "` + api.ServiceName + `", + APIVersion: "` + apiVersion + `", } } diff --git a/codegenerator/model/types.go b/codegenerator/model/types.go index 27bf17d..66b0094 100644 --- a/codegenerator/model/types.go +++ b/codegenerator/model/types.go @@ -63,7 +63,7 @@ type ( // See https://schemas.taskcluster.net/base/v1/api-reference.json#/properties/entries/items/properties/name Name string `json:"name"` - // One of: + // Any of: // * OutputSchema // * Blob // @@ -130,6 +130,13 @@ type ( // See https://schemas.taskcluster.net/base/v1/api-reference.json#/properties/$schema Schema string `json:"$schema"` + // Version of the API + // + // Syntax: ^v[0-9]+$ + // + // See https://schemas.taskcluster.net/base/v1/api-reference.json#/properties/apiVersion + APIVersion string `json:"apiVersion,omitempty"` + // BaseUrl for all _routes_ described in this document // // See https://schemas.taskcluster.net/base/v1/api-reference.json#/properties/baseUrl @@ -173,7 +180,7 @@ type ( // Possible values: // * "blob" // - // See https://schemas.taskcluster.net/base/v1/api-reference.json#/properties/entries/items/properties/output/oneOf[1] + // See https://schemas.taskcluster.net/base/v1/api-reference.json#/properties/entries/items/properties/output/anyOf[1] Blob string // if/then objects will replace themselves with the contents of then if the `if` is true @@ -268,7 +275,7 @@ type ( // JSON schema for output, if output is validated, otherwise not present. The value must be a relative URI, based on the service's schema location; that is, based at `/schemas/