Mirror of https://github.com/roostorg/osprey
github.com/roostorg/osprey
1use anyhow::{Context, Result};
2use async_trait::async_trait;
3use reqwest::Client;
4
5use crate::gcloud::auth::{Token, TokenRefreshFn, TokenRefresher};
6
7/// Taken from https://cloud.google.com/appengine/docs/standard/java/accessing-instance-metadata
8const METADATA_URL: &str = "http://metadata.google.internal/computeMetadata/v1";
9static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
10
11/// Provides helpers to get data about a GCP instance. This is done through accessing a http endpoint
12/// that contains metadata about the current service.
13///
14/// See https://cloud.google.com/appengine/docs/standard/java/accessing-instance-metadata
15#[derive(Debug, Clone)]
16pub struct GCPMetadataClient {
17 client: Client,
18 service_account: String,
19}
20
21impl GCPMetadataClient {
22 pub fn new(service_account: String) -> Result<Self> {
23 let client = Client::builder().user_agent(APP_USER_AGENT).build()?;
24 Ok(Self {
25 client,
26 service_account,
27 })
28 }
29
30 pub async fn get_access_token(&self) -> Result<Token> {
31 let url = format!(
32 "{}/instance/service-accounts/{}/token",
33 METADATA_URL, self.service_account
34 );
35
36 /*
37 We should see something like this:
38 {
39 "access_token":"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_QtAS08i85nHq39HE3C2LTrCARA",
40 "expires_in":3599,
41 "token_type":"Bearer"
42 }
43 */
44 let access_token = self
45 .client
46 .get(url)
47 .header("Metadata-Flavor", "Google")
48 .send()
49 .await?
50 .json::<Token>()
51 .await?;
52 Ok(access_token)
53 }
54}
55
56#[async_trait]
57impl TokenRefreshFn for GCPMetadataClient {
58 async fn get_token(&self) -> Result<Token> {
59 self.get_access_token().await
60 }
61}
62
63impl TokenRefresher<GCPMetadataClient> {
64 pub async fn with_metadata_client(client: GCPMetadataClient) -> Result<Self> {
65 Ok(Self::new(
66 client
67 .get_access_token()
68 .await
69 .context("Initial credential load")?,
70 client,
71 ))
72 }
73}