99 "net/url"
1010 "os"
1111 "path"
12+ "sync"
13+ "time"
1214
1315 arkapi "github.com/jetstack/preflight/internal/cyberark/api"
1416 "github.com/jetstack/preflight/pkg/version"
@@ -35,21 +37,34 @@ const (
3537// users to fetch URLs for various APIs available in CyberArk. This client is specialised to
3638// fetch only API endpoints, since only API endpoints are required by the Venafi Kubernetes Agent currently.
3739type Client struct {
38- client * http.Client
39- baseURL string
40+ client * http.Client
41+ baseURL string
42+ subdomain string
43+
44+ cachedResponse * Services
45+ cachedTenantID string
46+ cachedResponseTime time.Time
47+ cachedResponseMutex sync.Mutex
4048}
4149
4250// New creates a new CyberArk Service Discovery client. If the ARK_DISCOVERY_API
4351// environment variable is set, it is used as the base URL for the service
4452// discovery API. Otherwise, the production URL is used.
45- func New (httpClient * http.Client ) * Client {
53+ func New (httpClient * http.Client , subdomain string ) * Client {
4654 baseURL := os .Getenv ("ARK_DISCOVERY_API" )
4755 if baseURL == "" {
4856 baseURL = ProdDiscoveryAPIBaseURL
4957 }
58+
5059 client := & Client {
51- client : httpClient ,
52- baseURL : baseURL ,
60+ client : httpClient ,
61+ baseURL : baseURL ,
62+ subdomain : subdomain ,
63+
64+ cachedResponse : nil ,
65+ cachedTenantID : "" ,
66+ cachedResponseTime : time.Time {},
67+ cachedResponseMutex : sync.Mutex {},
5368 }
5469
5570 return client
@@ -93,17 +108,24 @@ type Services struct {
93108 DiscoveryContext ServiceEndpoint
94109}
95110
96- // DiscoverServices fetches from the service discovery service for a given subdomain
111+ // DiscoverServices fetches from the service discovery service for the configured subdomain
97112// and parses the CyberArk Identity API URL and Inventory API URL.
98113// It also returns the Tenant ID UUID corresponding to the subdomain.
99- func (c * Client ) DiscoverServices (ctx context.Context , subdomain string ) (* Services , string , error ) {
114+ func (c * Client ) DiscoverServices (ctx context.Context ) (* Services , string , error ) {
115+ c .cachedResponseMutex .Lock ()
116+ defer c .cachedResponseMutex .Unlock ()
117+
118+ if c .cachedResponse != nil && time .Since (c .cachedResponseTime ) < 1 * time .Hour {
119+ return c .cachedResponse , c .cachedTenantID , nil
120+ }
121+
100122 u , err := url .Parse (c .baseURL )
101123 if err != nil {
102124 return nil , "" , fmt .Errorf ("invalid base URL for service discovery: %w" , err )
103125 }
104126
105127 u .Path = path .Join (u .Path , "api/public/tenant-discovery" )
106- u .RawQuery = url.Values {"bySubdomain" : []string {subdomain }}.Encode ()
128+ u .RawQuery = url.Values {"bySubdomain" : []string {c . subdomain }}.Encode ()
107129
108130 endpoint := u .String ()
109131
@@ -127,7 +149,7 @@ func (c *Client) DiscoverServices(ctx context.Context, subdomain string) (*Servi
127149 // a 404 error is returned with an empty JSON body "{}" if the subdomain is unknown; at the time of writing, we haven't observed
128150 // any other errors and so we can't special case them
129151 if resp .StatusCode == http .StatusNotFound {
130- return nil , "" , fmt .Errorf ("got an HTTP 404 response from service discovery; maybe the subdomain %q is incorrect or does not exist?" , subdomain )
152+ return nil , "" , fmt .Errorf ("got an HTTP 404 response from service discovery; maybe the subdomain %q is incorrect or does not exist?" , c . subdomain )
131153 }
132154
133155 return nil , "" , fmt .Errorf ("got unexpected status code %s from request to service discovery API" , resp .Status )
@@ -167,8 +189,14 @@ func (c *Client) DiscoverServices(ctx context.Context, subdomain string) (*Servi
167189 }
168190 //TODO: Should add a check for discoveryContextAPI too?
169191
170- return & Services {
192+ services := & Services {
171193 Identity : ServiceEndpoint {API : identityAPI },
172194 DiscoveryContext : ServiceEndpoint {API : discoveryContextAPI },
173- }, discoveryResp .TenantID , nil
195+ }
196+
197+ c .cachedResponse = services
198+ c .cachedTenantID = discoveryResp .TenantID
199+ c .cachedResponseTime = time .Now ()
200+
201+ return services , discoveryResp .TenantID , nil
174202}
0 commit comments