Merge branch '9550-keep-services-env'
[arvados.git] / sdk / go / arvadosclient / arvadosclient.go
index 68c0632a309a1215bf93d03664f51c9d37bb4c67..aeb81f9317e871c81156ba759b163f9aeeb5f08e 100644 (file)
@@ -86,6 +86,12 @@ type ArvadosClient struct {
        // the client is outside the cluster.
        External bool
 
+       // Base URIs of Keep services, e.g., {"https://host1:8443",
+       // "https://host2:8443"}.  If this is nil, Keep clients will
+       // use the arvados.v1.keep_services.accessible API to discover
+       // available services.
+       KeepServiceURIs []string
+
        // Discovery document
        DiscoveryDoc Dict
 
@@ -95,9 +101,10 @@ type ArvadosClient struct {
        Retries int
 }
 
-// Create a new ArvadosClient, initialized with standard Arvados environment
-// variables ARVADOS_API_HOST, ARVADOS_API_TOKEN, and (optionally)
-// ARVADOS_API_HOST_INSECURE.
+// MakeArvadosClient creates a new ArvadosClient using the standard
+// environment variables ARVADOS_API_HOST, ARVADOS_API_TOKEN,
+// ARVADOS_API_HOST_INSECURE, ARVADOS_EXTERNAL_CLIENT, and
+// ARVADOS_KEEP_SERVICES.
 func MakeArvadosClient() (ac ArvadosClient, err error) {
        var matchTrue = regexp.MustCompile("^(?i:1|yes|true)$")
        insecure := matchTrue.MatchString(os.Getenv("ARVADOS_API_HOST_INSECURE"))
@@ -113,6 +120,18 @@ func MakeArvadosClient() (ac ArvadosClient, err error) {
                External: external,
                Retries:  2}
 
+       for _, s := range strings.Split(os.Getenv("ARVADOS_KEEP_SERVICES"), " ") {
+               if s == "" {
+                       continue
+               }
+               if u, err := url.Parse(s); err != nil {
+                       return ac, fmt.Errorf("ARVADOS_KEEP_SERVICES: %q: %s", s, err)
+               } else if !u.IsAbs() {
+                       return ac, fmt.Errorf("ARVADOS_KEEP_SERVICES: %q: not an absolute URI", s)
+               }
+               ac.KeepServiceURIs = append(ac.KeepServiceURIs, s)
+       }
+
        if ac.ApiServer == "" {
                return ac, MissingArvadosApiHost
        }
@@ -165,13 +184,13 @@ func (c ArvadosClient) CallRaw(method string, resourceType string, uuid string,
 
        retryable := false
        switch method {
-       case "GET", "HEAD", "PUT", "OPTIONS", "POST", "DELETE":
+       case "GET", "HEAD", "PUT", "OPTIONS", "DELETE":
                retryable = true
        }
 
-       // POST and DELETE are not safe to retry automatically, so we minimize
-       // such failures by always using a new or recently active socket
-       if method == "POST" || method == "DELETE" {
+       // Non-retryable methods such as POST are not safe to retry automatically,
+       // so we minimize such failures by always using a new or recently active socket
+       if !retryable {
                if time.Since(c.lastClosedIdlesAt) > MaxIdleConnectionDuration {
                        c.lastClosedIdlesAt = time.Now()
                        c.Client.Transport.(*http.Transport).CloseIdleConnections()
@@ -228,9 +247,8 @@ func (c ArvadosClient) CallRaw(method string, resourceType string, uuid string,
 
        if resp != nil {
                return nil, newAPIServerError(c.ApiServer, resp)
-       } else {
-               return nil, err
        }
+       return nil, err
 }
 
 func newAPIServerError(ServerAddress string, resp *http.Response) APIServerError {
@@ -274,7 +292,7 @@ func newAPIServerError(ServerAddress string, resp *http.Response) APIServerError
 // Returns a non-nil error if an error occurs making the API call, the
 // API responds with a non-successful HTTP status, or an error occurs
 // parsing the response body.
-func (c ArvadosClient) Call(method string, resourceType string, uuid string, action string, parameters Dict, output interface{}) error {
+func (c ArvadosClient) Call(method, resourceType, uuid, action string, parameters Dict, output interface{}) error {
        reader, err := c.CallRaw(method, resourceType, uuid, action, parameters)
        if reader != nil {
                defer reader.Close()