blob: 57e2a81f4f0a725c8983aa839e5eded9d795d80b [file] [log] [blame]
Ash Wilsonc4360202014-08-29 14:14:24 -04001package utils
2
3import (
4 "fmt"
Ash Wilsona0c4c842014-09-09 11:30:58 -04005 "net/url"
Ash Wilsonaef9dc52014-08-29 14:21:26 -04006 "strings"
Ash Wilsonc4360202014-08-29 14:14:24 -04007
8 "github.com/racker/perigee"
9)
10
11// Version is a supported API version, corresponding to a vN package within the appropriate service.
12type Version struct {
13 ID string
14 Priority int
15}
16
Ash Wilsonaef9dc52014-08-29 14:21:26 -040017var goodStatus = map[string]bool{
18 "current": true,
19 "supported": true,
20 "stable": true,
21}
22
Ash Wilsonc4360202014-08-29 14:14:24 -040023// ChooseVersion queries the base endpoint of a API to choose the most recent non-experimental alternative from a service's
24// published versions.
25// It returns the highest-Priority Version among the alternatives that are provided, as well as its corresponding endpoint.
Ash Wilsona0c4c842014-09-09 11:30:58 -040026func ChooseVersion(identityEndpoint string, recognized []*Version) (*Version, string, error) {
Ash Wilsonc4360202014-08-29 14:14:24 -040027 type linkResp struct {
28 Href string `json:"href"`
29 Rel string `json:"rel"`
30 }
31
32 type valueResp struct {
33 ID string `json:"id"`
34 Status string `json:"status"`
35 Links []linkResp `json:"links"`
36 }
37
38 type versionsResp struct {
39 Values []valueResp `json:"values"`
40 }
41
42 type response struct {
43 Versions versionsResp `json:"versions"`
44 }
45
Ash Wilsona0c4c842014-09-09 11:30:58 -040046 // Normalize the identity endpoint that's provided by trimming any path, query or fragment from the URL.
47 u, err := url.Parse(identityEndpoint)
48 if err != nil {
49 return nil, "", err
50 }
51 u.Path, u.RawQuery, u.Fragment = "", "", ""
52 normalized := u.String()
53
Ash Wilsonc4360202014-08-29 14:14:24 -040054 var resp response
Ash Wilsona0c4c842014-09-09 11:30:58 -040055 _, err = perigee.Request("GET", normalized, perigee.Options{
Ash Wilsonc4360202014-08-29 14:14:24 -040056 Results: &resp,
Ash Wilson72f7dfb2014-09-02 14:06:00 -040057 OkCodes: []int{200, 300},
Ash Wilsonc4360202014-08-29 14:14:24 -040058 })
59
60 if err != nil {
61 return nil, "", err
62 }
63
64 byID := make(map[string]*Version)
65 for _, version := range recognized {
66 byID[version.ID] = version
67 }
68
69 var highest *Version
70 var endpoint string
71
Ash Wilsonccfccce2014-09-09 11:43:04 -040072 normalize := func(endpoint string) string {
73 if !strings.HasSuffix(endpoint, "/") {
74 return endpoint + "/"
75 }
76 return endpoint
77 }
78
Ash Wilsonc4360202014-08-29 14:14:24 -040079 for _, value := range resp.Versions.Values {
Ash Wilsona0c4c842014-09-09 11:30:58 -040080 href := ""
81 for _, link := range value.Links {
82 if link.Rel == "self" {
83 href = link.Href
84 }
85 }
Ash Wilsonc4360202014-08-29 14:14:24 -040086
Ash Wilsona0c4c842014-09-09 11:30:58 -040087 if matching, ok := byID[value.ID]; ok {
88 // Prefer a version that exactly matches the provided endpoint.
Ash Wilsonccfccce2014-09-09 11:43:04 -040089 if normalize(href) == normalize(identityEndpoint) {
Ash Wilsona0c4c842014-09-09 11:30:58 -040090 if href == "" {
91 return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", value.ID, normalized)
Ash Wilsonc4360202014-08-29 14:14:24 -040092 }
Ash Wilsona0c4c842014-09-09 11:30:58 -040093 return matching, href, nil
94 }
Ash Wilsonc4360202014-08-29 14:14:24 -040095
Ash Wilsona0c4c842014-09-09 11:30:58 -040096 // Otherwise, find the highest-priority version with a whitelisted status.
97 if goodStatus[strings.ToLower(value.Status)] {
98 if highest == nil || matching.Priority > highest.Priority {
99 highest = matching
100 endpoint = href
Ash Wilsonc4360202014-08-29 14:14:24 -0400101 }
102 }
103 }
104 }
105
Ash Wilsona0c4c842014-09-09 11:30:58 -0400106 if highest == nil {
107 return nil, "", fmt.Errorf("No supported version available from endpoint %s", normalized)
108 }
109 if endpoint == "" {
110 return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", highest.ID, normalized)
Ash Wilsonc4360202014-08-29 14:14:24 -0400111 }
112
113 return highest, endpoint, nil
114}