/
aks.go
159 lines (133 loc) · 4.04 KB
/
aks.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Package aks provides a datagatherer for AKS.
package aks
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
aks "github.com/Azure/aks-engine/pkg/api/agentPoolOnlyApi/v20180331"
"github.com/jetstack/preflight/pkg/datagatherer"
)
// Config is the configuration for an AKS DataGatherer.
type Config struct {
// ClusterName is the name of the cluster in AKS.
ClusterName string `yaml:"cluster-name"`
// ResourceGroup is the resource group the cluster belongs to.
ResourceGroup string `yaml:"resource-group"`
// CredentialsPath is the path to the json file containing the credentials to access Azure APIs.
CredentialsPath string `yaml:"credentials-path"`
}
// validate checks if a Config is valid.
func (c *Config) validate() error {
errs := []string{}
msg := "%s should be a non empty string."
if c.ClusterName == "" {
errs = append(errs, fmt.Sprintf(msg, "ClusterName"))
}
if c.ResourceGroup == "" {
errs = append(errs, fmt.Sprintf(msg, "ResourceGroup"))
}
if c.CredentialsPath == "" {
errs = append(errs, fmt.Sprintf(msg, "CredentialsPath"))
}
if len(errs) == 0 {
return nil
}
return fmt.Errorf("invalid configuration: %s", strings.Join(errs, ";"))
}
// NewDataGatherer creates a new AKS DataGatherer. It performs a config validation.
func (c *Config) NewDataGatherer(ctx context.Context) (datagatherer.DataGatherer, error) {
if err := c.validate(); err != nil {
return nil, err
}
credentials, err := readCredentials(c.CredentialsPath)
if err != nil {
return nil, err
}
return &DataGatherer{
resourceGroup: c.ResourceGroup,
clusterName: c.ClusterName,
credentials: credentials,
}, nil
}
// AzureCredentials contains credentials needed to authenticate against Azure APIs.
type AzureCredentials struct {
AccessToken string `json:"accessToken"`
ExpiresOn string `json:"expiresOn"`
Subscription string `json:"subscription"`
Tenant string `json:"tenant"`
TokenType string `json:"tokenType"`
}
func readCredentials(path string) (*AzureCredentials, error) {
b, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var creds AzureCredentials
err = json.Unmarshal(b, &creds)
if err != nil {
return nil, err
}
if len(creds.Subscription) == 0 {
return nil, fmt.Errorf("'subscription' must not be empty")
}
if creds.TokenType != "Bearer" {
return nil, fmt.Errorf("'tokenType' %s is not supported", creds.TokenType)
}
return &creds, nil
}
// DataGatherer is a data-gatherer for AKS.
type DataGatherer struct {
resourceGroup string
clusterName string
credentials *AzureCredentials
}
// Info contains the data retrieved from AKS.
type Info struct {
// Cluster represents an AKS cluster.
Cluster *aks.ManagedCluster
}
func (g *DataGatherer) Run(stopCh <-chan struct{}) error {
// no async functionality, see Fetch
return nil
}
func (g *DataGatherer) Delete() error {
// no async functionality, see Fetch
return nil
}
func (g *DataGatherer) WaitForCacheSync(stopCh <-chan struct{}) error {
// no async functionality, see Fetch
return nil
}
// Fetch retrieves cluster information from AKS.
func (g *DataGatherer) Fetch() (interface{}, error) {
client := &http.Client{}
req, err := http.NewRequest("GET", fmt.Sprintf("https://management.azure.com/subscriptions/%s/resourceGroups/%s/providers/Microsoft.ContainerService/managedClusters/%s?api-version=2019-08-01", g.credentials.Subscription, g.resourceGroup, g.clusterName), nil)
if err != nil {
return nil, err
}
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", g.credentials.AccessToken))
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
errorBody, _ := ioutil.ReadAll(resp.Body)
return nil, fmt.Errorf("error retrieving cluster information (status code %d): %v", resp.StatusCode, string(errorBody))
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var cluster aks.ManagedCluster
err = json.Unmarshal(body, &cluster)
if err != nil {
return nil, err
}
return &Info{
Cluster: &cluster,
}, nil
}