@@ -26,7 +26,8 @@ const (
2626
2727// TokenResponse represents the response from the metadata service
2828type TokenResponse struct {
29- Token string `json:"token"`
29+ Token string `json:"token"`
30+ CloudAPIUrl string `json:"cloudAPIUrl"`
3031}
3132
3233// Client handles communication with the metadata service with token caching
@@ -35,9 +36,10 @@ type Client struct {
3536 httpClient * http.Client
3637 logger * zap.Logger
3738
38- // Token caching
39+ // Token and CloudAPI URL caching
3940 tokenMu sync.RWMutex
4041 cachedToken string
42+ cachedAPIURL string
4143 tokenExpiry time.Time
4244 tokenLifetime time.Duration
4345}
@@ -81,30 +83,31 @@ func (c *Client) GetAuthToken(ctx context.Context, vmID string) (string, error)
8183 return c .cachedToken , nil
8284 }
8385
84- // Fetch new token
85- token , err := c .fetchAuthToken (ctx , vmID )
86+ // Fetch new token and metadata
87+ tokenResp , err := c .fetchAuthToken (ctx , vmID )
8688 if err != nil {
8789 return "" , err
8890 }
8991
90- // Cache the token
91- c .cachedToken = token
92+ // Cache the token and CloudAPI URL
93+ c .cachedToken = tokenResp .Token
94+ c .cachedAPIURL = tokenResp .CloudAPIUrl
9295 c .tokenExpiry = time .Now ().Add (c .tokenLifetime )
9396
9497 c .logger .Info ("Successfully fetched and cached new auth token" ,
9598 zap .Time ("expires_at" , c .tokenExpiry ),
9699 zap .Duration ("lifetime" , c .tokenLifetime ))
97100
98- return token , nil
101+ return tokenResp . Token , nil
99102}
100103
101- // fetchAuthToken fetches a new authentication token from the metadata service
102- func (c * Client ) fetchAuthToken (ctx context.Context , vmID string ) (string , error ) {
104+ // fetchAuthToken fetches a new authentication token and metadata
105+ func (c * Client ) fetchAuthToken (ctx context.Context , vmID string ) (* TokenResponse , error ) {
103106 c .logger .Debug ("Fetching new auth token from metadata service" , zap .String ("endpoint" , c .endpoint ))
104107
105108 req , err := http .NewRequestWithContext (ctx , http .MethodGet , c .endpoint , nil )
106109 if err != nil {
107- return "" , fmt .Errorf ("failed to create request: %w" , err )
110+ return nil , fmt .Errorf ("failed to create request: %w" , err )
108111 }
109112
110113 req .Header .Set (HeaderAccept , AcceptJSON )
@@ -113,7 +116,7 @@ func (c *Client) fetchAuthToken(ctx context.Context, vmID string) (string, error
113116 resp , err := c .httpClient .Do (req )
114117 if err != nil {
115118 c .logger .Error ("Failed to fetch auth token" , zap .Error (err ))
116- return "" , fmt .Errorf ("failed to fetch auth token: %w" , err )
119+ return nil , fmt .Errorf ("failed to fetch auth token: %w" , err )
117120 }
118121 defer func () {
119122 if closeErr := resp .Body .Close (); closeErr != nil {
@@ -126,25 +129,25 @@ func (c *Client) fetchAuthToken(ctx context.Context, vmID string) (string, error
126129 c .logger .Error ("Metadata service returned error" ,
127130 zap .Int ("status_code" , resp .StatusCode ),
128131 zap .String ("response" , string (body )))
129- return "" , fmt .Errorf ("metadata service returned status %d: %s" , resp .StatusCode , string (body ))
132+ return nil , fmt .Errorf ("metadata service returned status %d: %s" , resp .StatusCode , string (body ))
130133 }
131134
132135 body , err := io .ReadAll (resp .Body )
133136 if err != nil {
134- return "" , fmt .Errorf ("failed to read response body: %w" , err )
137+ return nil , fmt .Errorf ("failed to read response body: %w" , err )
135138 }
136139
137140 var tokenResp TokenResponse
138141 if err := json .Unmarshal (body , & tokenResp ); err != nil {
139- return "" , fmt .Errorf ("failed to unmarshal token response: %w" , err )
142+ return nil , fmt .Errorf ("failed to unmarshal token response: %w" , err )
140143 }
141144
142145 if tokenResp .Token == "" {
143- return "" , fmt .Errorf ("received empty token from metadata service" )
146+ return nil , fmt .Errorf ("received empty token from metadata service" )
144147 }
145148
146- c .logger .Debug ("Successfully fetched auth token" )
147- return tokenResp . Token , nil
149+ c .logger .Debug ("Successfully fetched auth token and metadata " )
150+ return & tokenResp , nil
148151}
149152
150153// GetAuthTokenWithRetry fetches an auth token with retry logic similar to tsclient
@@ -191,10 +194,39 @@ func (c *Client) InvalidateCache() {
191194 defer c .tokenMu .Unlock ()
192195
193196 c .cachedToken = ""
197+ c .cachedAPIURL = ""
194198 c .tokenExpiry = time.Time {}
195199 c .logger .Debug ("Token cache invalidated" )
196200}
197201
202+ // GetCloudAPIURL returns the cached CloudAPI URL
203+ func (c * Client ) GetCloudAPIURL (ctx context.Context , vmID string ) (string , error ) {
204+ c .tokenMu .RLock ()
205+ if c .cachedAPIURL != "" && time .Now ().Before (c .tokenExpiry ) {
206+ url := c .cachedAPIURL
207+ c .tokenMu .RUnlock ()
208+ c .logger .Debug ("Using cached CloudAPI URL" , zap .String ("url" , url ))
209+ return url , nil
210+ }
211+ c .tokenMu .RUnlock ()
212+
213+ // Need to fetch new metadata to get the URL
214+ _ , err := c .GetAuthToken (ctx , vmID )
215+ if err != nil {
216+ return "" , fmt .Errorf ("failed to get CloudAPI URL: %w" , err )
217+ }
218+
219+ c .tokenMu .RLock ()
220+ url := c .cachedAPIURL
221+ c .tokenMu .RUnlock ()
222+
223+ if url == "" {
224+ return "" , fmt .Errorf ("CloudAPI URL not available in metadata response" )
225+ }
226+
227+ return url , nil
228+ }
229+
198230// Close closes the HTTP client
199231func (c * Client ) Close () error {
200232 c .httpClient .CloseIdleConnections ()
0 commit comments