2
0
Эх сурвалжийг харах

Merge pull request #491 from kubecost/AjayTripathy-aws-keyinfo

Ajay tripathy aws keyinfo
Ajay Tripathy 5 жил өмнө
parent
commit
ba36707ee1

+ 63 - 3
pkg/cloud/awsprovider.go

@@ -104,6 +104,7 @@ type AWS struct {
 	ProjectID                   string
 	DownloadPricingDataLock     sync.RWMutex
 	Config                      *ProviderConfig
+	ServiceAccountChecks        map[string]*ServiceAccountCheck
 	*CustomProvider
 }
 
@@ -394,7 +395,6 @@ func (aws *AWS) UpdateConfig(r io.Reader, updateType string) (*CustomPricing, er
 				return err
 			}
 		}
-
 		return nil
 	})
 }
@@ -515,6 +515,9 @@ func (aws *AWS) isPreemptible(key string) bool {
 func (aws *AWS) DownloadPricingData() error {
 	aws.DownloadPricingDataLock.Lock()
 	defer aws.DownloadPricingDataLock.Unlock()
+	if aws.ServiceAccountChecks == nil {
+		aws.ServiceAccountChecks = make(map[string]*ServiceAccountCheck)
+	}
 	c, err := aws.Config.GetCustomPricingData()
 	if err != nil {
 		klog.V(1).Infof("Error downloading default pricing data: %s", err.Error())
@@ -795,7 +798,7 @@ func (aws *AWS) refreshSpotPricing(force bool) {
 		return
 	}
 
-	sp, err := parseSpotData(aws.SpotDataBucket, aws.SpotDataPrefix, aws.ProjectID, aws.SpotDataRegion, aws.ServiceKeyName, aws.ServiceKeySecret)
+	sp, err := aws.parseSpotData(aws.SpotDataBucket, aws.SpotDataPrefix, aws.ProjectID, aws.SpotDataRegion, aws.ServiceKeyName, aws.ServiceKeySecret)
 	if err != nil {
 		klog.V(1).Infof("Skipping AWS spot data download: %s", err.Error())
 		return
@@ -1093,18 +1096,41 @@ func (awsProvider *AWS) ClusterInfo() (map[string]string, error) {
 
 // Gets the aws key id and secret
 func (aws *AWS) getAWSAuth(forceReload bool, cp *CustomPricing) (string, string) {
+	if aws.ServiceAccountChecks == nil { // safety in case checks don't exist
+		aws.ServiceAccountChecks = make(map[string]*ServiceAccountCheck)
+	}
+
 	// 1. Check config values first (set from frontend UI)
 	if cp.ServiceKeyName != "" && cp.ServiceKeySecret != "" {
+		aws.ServiceAccountChecks["hasKey"] = &ServiceAccountCheck{
+			Message: "ServiceKey exists",
+			Status:  true,
+		}
 		return cp.ServiceKeyName, cp.ServiceKeySecret
 	}
 
 	// 2. Check for secret
 	s, _ := aws.loadAWSAuthSecret(forceReload)
 	if s != nil && s.AccessKeyID != "" && s.SecretAccessKey != "" {
+		aws.ServiceAccountChecks["hasKey"] = &ServiceAccountCheck{
+			Message: "ServiceKey exists",
+			Status:  true,
+		}
 		return s.AccessKeyID, s.SecretAccessKey
 	}
 
 	// 3. Fall back to env vars
+	if env.GetAWSAccessKeyID() == "" || env.GetAWSAccessKeyID() == "" {
+		aws.ServiceAccountChecks["hasKey"] = &ServiceAccountCheck{
+			Message: "ServiceKey exists",
+			Status:  false,
+		}
+	} else {
+		aws.ServiceAccountChecks["hasKey"] = &ServiceAccountCheck{
+			Message: "ServiceKey exists",
+			Status:  true,
+		}
+	}
 	return env.GetAWSAccessKeyID(), env.GetAWSAccessKeySecret()
 }
 
@@ -1955,7 +1981,11 @@ func (f fnames) Less(i, j int) bool {
 	return t1.Before(t2)
 }
 
-func parseSpotData(bucket string, prefix string, projectID string, region string, accessKeyID string, accessKeySecret string) (map[string]*spotInfo, error) {
+func (a *AWS) parseSpotData(bucket string, prefix string, projectID string, region string, accessKeyID string, accessKeySecret string) (map[string]*spotInfo, error) {
+	if a.ServiceAccountChecks == nil { // Set up checks to store error/success states
+		a.ServiceAccountChecks = make(map[string]*ServiceAccountCheck)
+	}
+
 	// credentials may exist on the actual AWS node-- if so, use those. If not, override with the service key
 	if accessKeyID != "" && accessKeySecret != "" {
 		err := env.Set(env.AWSAccessKeyIDEnvVar, accessKeyID)
@@ -1990,7 +2020,17 @@ func parseSpotData(bucket string, prefix string, projectID string, region string
 	}
 	lso, err := s3Svc.ListObjects(ls)
 	if err != nil {
+		a.ServiceAccountChecks["bucketList"] = &ServiceAccountCheck{
+			Message:        "Bucket List Permissions Available",
+			Status:         false,
+			AdditionalInfo: err.Error(),
+		}
 		return nil, err
+	} else {
+		a.ServiceAccountChecks["bucketList"] = &ServiceAccountCheck{
+			Message: "Bucket List Permissions Available",
+			Status:  true,
+		}
 	}
 	lsoLen := len(lso.Contents)
 	klog.V(2).Infof("Found %d spot data files from yesterday", lsoLen)
@@ -2033,7 +2073,17 @@ func parseSpotData(bucket string, prefix string, projectID string, region string
 		buf := aws.NewWriteAtBuffer([]byte{})
 		_, err := downloader.Download(buf, getObj)
 		if err != nil {
+			a.ServiceAccountChecks["objectList"] = &ServiceAccountCheck{
+				Message:        "Object Get Permissions Available",
+				Status:         false,
+				AdditionalInfo: err.Error(),
+			}
 			return nil, err
+		} else {
+			a.ServiceAccountChecks["objectList"] = &ServiceAccountCheck{
+				Message: "Object Get Permissions Available",
+				Status:  true,
+			}
 		}
 
 		r := bytes.NewReader(buf.Bytes())
@@ -2335,3 +2385,13 @@ func (a *AWS) getReservedInstances() ([]*AWSReservedInstance, error) {
 
 	return reservedInstances, nil
 }
+
+func (a *AWS) ServiceAccountStatus() *ServiceAccountStatus {
+	checks := []*ServiceAccountCheck{}
+	for _, v := range a.ServiceAccountChecks {
+		checks = append(checks, v)
+	}
+	return &ServiceAccountStatus{
+		Checks: checks,
+	}
+}

+ 6 - 0
pkg/cloud/azureprovider.go

@@ -774,3 +774,9 @@ func (az *Azure) PVPricing(pvk PVKey) (*PV, error) {
 func (az *Azure) GetLocalStorageQuery(window, offset string, rate bool, used bool) string {
 	return ""
 }
+
+func (az *Azure) ServiceAccountStatus() *ServiceAccountStatus {
+	return &ServiceAccountStatus{
+		Checks: []*ServiceAccountCheck{},
+	}
+}

+ 6 - 0
pkg/cloud/csvprovider.go

@@ -288,3 +288,9 @@ func (c *CSVProvider) PVPricing(pvk PVKey) (*PV, error) {
 		Cost: pricing.MarketPriceHourly,
 	}, nil
 }
+
+func (c *CSVProvider) ServiceAccountStatus() *ServiceAccountStatus {
+	return &ServiceAccountStatus{
+		Checks: []*ServiceAccountCheck{},
+	}
+}

+ 6 - 0
pkg/cloud/customprovider.go

@@ -262,3 +262,9 @@ func (cpk *customProviderKey) Features() string {
 	}
 	return "default" // TODO: multiple custom pricing support.
 }
+
+func (cp *CustomProvider) ServiceAccountStatus() *ServiceAccountStatus {
+	return &ServiceAccountStatus{
+		Checks: []*ServiceAccountCheck{},
+	}
+}

+ 6 - 0
pkg/cloud/gcpprovider.go

@@ -1379,3 +1379,9 @@ func (gcp *GCP) NodePricing(key Key) (*Node, error) {
 	}
 	return nil, fmt.Errorf("Warning: no pricing data found for %s", key)
 }
+
+func (gcp *GCP) ServiceAccountStatus() *ServiceAccountStatus {
+	return &ServiceAccountStatus{
+		Checks: []*ServiceAccountCheck{},
+	}
+}

+ 11 - 0
pkg/cloud/provider.go

@@ -158,6 +158,16 @@ type CustomPricing struct {
 	ReadOnly              string            `json:"readOnly"`
 }
 
+type ServiceAccountStatus struct {
+	Checks []*ServiceAccountCheck `json:"checks"`
+}
+
+type ServiceAccountCheck struct {
+	Message        string `json:"message"`
+	Status         bool   `json:"status"`
+	AdditionalInfo string `json:additionalInfo`
+}
+
 // Provider represents a k8s provider.
 type Provider interface {
 	ClusterInfo() (map[string]string, error)
@@ -177,6 +187,7 @@ type Provider interface {
 	GetLocalStorageQuery(string, string, bool, bool) string
 	ExternalAllocations(string, string, []string, string, string, bool) ([]*OutOfClusterAllocation, error)
 	ApplyReservedInstancePricing(map[string]*Node)
+	ServiceAccountStatus() *ServiceAccountStatus
 }
 
 // ClusterName returns the name defined in cluster info, defaulting to the

+ 1 - 1
pkg/costmodel/costmodel.go

@@ -1137,7 +1137,7 @@ func (cm *CostModel) GetNodeCost(cp costAnalyzerCloud.Provider) (map[string]*cos
 
 		cnode, err := cp.NodePricing(cp.GetKey(nodeLabels, n))
 		if err != nil {
-			log.DedupedWarningf(10, "Error getting node pricing. Error: %s", err.Error())
+			klog.Infof("Error getting node pricing. Error: %s", err.Error())
 			if cnode != nil {
 				nodes[name] = cnode
 				continue

+ 8 - 0
pkg/costmodel/router.go

@@ -644,6 +644,13 @@ func (p *Accesses) ClusterInfo(w http.ResponseWriter, r *http.Request, ps httpro
 	w.Write(WrapData(data, err))
 }
 
+func (p *Accesses) GetServiceAccountStatus(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+
+	w.Write(WrapData(A.Cloud.ServiceAccountStatus(), nil))
+}
+
 func (p *Accesses) GetPrometheusMetadata(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Access-Control-Allow-Origin", "*")
@@ -1237,6 +1244,7 @@ func Initialize(additionalConfigWatchers ...ConfigWatchers) {
 	Router.GET("/managementPlatform", A.ManagementPlatform)
 	Router.GET("/clusterInfo", A.ClusterInfo)
 	Router.GET("/clusters", managerEndpoints.GetAllClusters)
+	Router.GET("/serviceAccountStatus", A.GetServiceAccountStatus)
 	Router.PUT("/clusters", managerEndpoints.PutCluster)
 	Router.DELETE("/clusters/:id", managerEndpoints.DeleteCluster)
 }