github.com/wunderio/silta-cli@v0.0.0-20240508100559-3017e4ab3a20/internal/azure/ciAuth.go (about)

     1  package azure
     2  
     3  import (
     4  	b64 "encoding/base64"
     5  	"encoding/json"
     6  	"errors"
     7  	"io"
     8  	"net/http"
     9  	"strings"
    10  )
    11  
    12  type Base64Kubeconfig struct {
    13  	Base64Kubeconfig string `json:"value"`
    14  	Name             string `json:"name"`
    15  }
    16  
    17  type Subscription struct {
    18  	SubscriptionID string `json:"subscriptionId"`
    19  }
    20  
    21  // OAuth 2 token structure
    22  type tokenResponse struct {
    23  	AccessToken  string  `json:"access_token"`
    24  	TokenType    string  `json:"token_type"`
    25  	ExpiresIn    float64 `json:"expires_in"`
    26  	ExtExpiresIn float64 `json:"ext_expires_in"`
    27  }
    28  
    29  // Returns the first subscription ID
    30  func GetDefaultSubscriptionID(token string) (subscriptionID string, err error) {
    31  
    32  	req, err := http.NewRequest(http.MethodGet, "https://management.azure.com/subscriptions?api-version=2020-01-01", nil)
    33  	if err != nil {
    34  		return "", err
    35  	}
    36  	req.Header.Set("Authorization", "Bearer "+token)
    37  
    38  	resp, err := http.DefaultClient.Do(req)
    39  	if err != nil {
    40  		return "", err
    41  	}
    42  	defer resp.Body.Close()
    43  
    44  	var subscriptions struct {
    45  		Value []Subscription `json:"value"`
    46  	}
    47  	if err := json.NewDecoder(resp.Body).Decode(&subscriptions); err != nil {
    48  		return "", err
    49  	}
    50  
    51  	if len(subscriptions.Value) == 0 {
    52  		return "", errors.New("no subscriptions found")
    53  	}
    54  	return subscriptions.Value[0].SubscriptionID, nil
    55  }
    56  
    57  // Returns access token. Failing that, returns non-nil error
    58  // tenantId - Azure tenant ID
    59  // clientId - Client ID. Can pass Service Principal ID
    60  // clientSecret - Client secret. Cant pass Service Principal password
    61  func GetAuthToken(tenantId string, clientId string, clientSecret string) (string, error) {
    62  	req, err := http.NewRequest(http.MethodPost, "https://login.microsoftonline.com/"+tenantId+"/oauth2/v2.0/token", nil)
    63  	if err != nil {
    64  		return "", err
    65  	}
    66  
    67  	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    68  
    69  	q := req.URL.Query()
    70  	q.Add("grant_type", "client_credentials")
    71  	q.Add("client_id", clientId)
    72  	q.Add("client_secret", clientSecret)
    73  	q.Add("scope", "https://management.azure.com/.default")
    74  
    75  	req.Body = io.NopCloser(strings.NewReader(q.Encode()))
    76  	resp, err := http.DefaultClient.Do(req)
    77  	if err != nil {
    78  		return "", err
    79  	}
    80  	defer resp.Body.Close()
    81  
    82  	str, err := io.ReadAll(resp.Body)
    83  	if err != nil {
    84  		return "", err
    85  	}
    86  
    87  	var token tokenResponse
    88  	if err := json.Unmarshal(str, &token); err != nil {
    89  		return "", err
    90  	}
    91  	return token.AccessToken, nil
    92  }
    93  
    94  func GetKubeconfig(accessToken string, subscriptionId string, resourceGroupName string, clusterName string) ([]byte, error) {
    95  	req, err := http.NewRequest(http.MethodPost, "https://management.azure.com/subscriptions/"+subscriptionId+"/resourceGroups/"+resourceGroupName+"/providers/Microsoft.ContainerService/managedClusters/"+clusterName+"/listClusterAdminCredential?api-version=2023-02-01", nil)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	req.Header.Set("Authorization", "Bearer "+accessToken)
   101  
   102  	resp, err := http.DefaultClient.Do(req)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  	defer resp.Body.Close()
   107  
   108  	var kubeconfigs struct {
   109  		Value []Base64Kubeconfig `json:"kubeconfigs"`
   110  	}
   111  	if err := json.NewDecoder(resp.Body).Decode(&kubeconfigs); err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	if len(kubeconfigs.Value) == 0 {
   116  		return nil, errors.New("no kubeconfigs found")
   117  	}
   118  
   119  	kconfigByte, err := b64.StdEncoding.DecodeString(kubeconfigs.Value[0].Base64Kubeconfig)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	return kconfigByte, nil
   124  }