Răsfoiți Sursa

Merge pull request #1488 from avrodrigues5/avr/wip-aliyun-integ

Support Download pricing + Node pricing on Aliyun aka Alibaba cloud provider
Michael Dresser 3 ani în urmă
părinte
comite
eb9fd0992b

+ 1 - 0
Dockerfile

@@ -37,5 +37,6 @@ ADD ./configs/default.json /models/default.json
 ADD ./configs/azure.json /models/azure.json
 ADD ./configs/aws.json /models/aws.json
 ADD ./configs/gcp.json /models/gcp.json
+ADD ./configs/alibaba.json /models/alibaba.json
 USER 1001
 ENTRYPOINT ["/go/bin/app"]

+ 12 - 0
configs/alibaba.json

@@ -0,0 +1,12 @@
+{
+    "provider": "Alibaba",
+    "description": "Default prices used to compute allocation between RAM and CPU. Alibaba Cloud pricing API data still used for total node cost.",
+    "alibabaServiceKeyName": "ABC",
+    "alibabaServiceKeySecret": "XYZ",
+    "CPU": "0.031611",
+    "spotCPU": "0.006655",
+    "RAM": "0.004237",
+    "GPU": "0.95",
+    "spotRAM": "0.000892",
+    "storage": "0.00005479452"
+}

+ 3 - 1
go.mod

@@ -63,6 +63,7 @@ require (
 	github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
 	github.com/Azure/go-autorest/logger v0.2.1 // indirect
 	github.com/Azure/go-autorest/tracing v0.6.0 // indirect
+	github.com/aliyun/alibaba-cloud-sdk-go v1.62.3 // indirect
 	github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.2.0 // indirect
 	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.4 // indirect
@@ -108,6 +109,7 @@ require (
 	github.com/mitchellh/mapstructure v1.4.1 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
 	github.com/pelletier/go-toml v1.9.3 // indirect
 	github.com/prometheus/procfs v0.7.3 // indirect
 	github.com/rs/xid v1.3.0 // indirect
@@ -133,7 +135,7 @@ require (
 	google.golang.org/grpc v1.38.0 // indirect
 	google.golang.org/protobuf v1.26.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
-	gopkg.in/ini.v1 v1.62.0 // indirect
+	gopkg.in/ini.v1 v1.67.0 // indirect
 	k8s.io/klog/v2 v2.4.0 // indirect
 	k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.0.2 // indirect

+ 12 - 0
go.sum

@@ -93,6 +93,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
+github.com/aliyun/alibaba-cloud-sdk-go v1.62.3 h1:kWY5c/9JOhSYBogi3mtNG7G9TxXS0CddtQ6RKOI3mvY=
+github.com/aliyun/alibaba-cloud-sdk-go v1.62.3/go.mod h1:Api2AkmMgGaSUAhmk76oaFObkoeCPc/bKAqcyplPODs=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
@@ -241,6 +243,7 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
 github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
 github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ=
@@ -374,6 +377,7 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
 github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
 github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
 github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
+github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -488,6 +492,8 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
 github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
+github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
 github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
@@ -588,6 +594,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
+github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
@@ -625,6 +633,7 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
 go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
 go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1031,6 +1040,9 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
 gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

+ 844 - 0
pkg/cloud/aliyunprovider.go

@@ -0,0 +1,844 @@
+package cloud
+
+import (
+	"fmt"
+	"io"
+	"io/ioutil"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
+	"github.com/opencost/opencost/pkg/clustercache"
+	"github.com/opencost/opencost/pkg/env"
+	"github.com/opencost/opencost/pkg/kubecost"
+	"github.com/opencost/opencost/pkg/log"
+	"github.com/opencost/opencost/pkg/util/fileutil"
+	"github.com/opencost/opencost/pkg/util/json"
+	"github.com/opencost/opencost/pkg/util/stringutil"
+	"golang.org/x/exp/slices"
+	v1 "k8s.io/api/core/v1"
+)
+
+const (
+	ALIBABA_ECS_PRODUCT_CODE                   = "ecs"
+	ALIBABA_ECS_VERSION                        = "2014-05-26"
+	ALIBABA_ECS_DOMAIN                         = "ecs.aliyuncs.com"
+	ALIBABA_DESCRIBE_PRICE_API_ACTION          = "DescribePrice"
+	ALIBABA_INSTANCE_RESOURCE_TYPE             = "instance"
+	ALIBABA_DISK_RESOURCE_TYPE                 = "disk"
+	ALIBABA_PAY_AS_YOU_GO_BILLING              = "Pay-As-You-Go"
+	ALIBABA_SUBSCRIPTION_BILLING               = "Subscription"
+	ALIBABA_PREEMPTIBLE_BILLING                = "Preemptible"
+	ALIBABA_OPTIMIZE_KEYWORD                   = "optimize"
+	ALIBABA_NON_OPTIMIZE_KEYWORD               = "nonoptimize"
+	ALIBABA_HOUR_PRICE_UNIT                    = "Hour"
+	ALIBABA_MONTH_PRICE_UNIT                   = "Month"
+	ALIBABA_YEAR_PRICE_UNIT                    = "Year"
+	ALIBABA_UNKNOWN_INSTANCE_FAMILY_TYPE       = "unknown"
+	ALIBABA_NOT_SUPPORTED_INSTANCE_FAMILY_TYPE = "unsupported"
+	ALIBABA_ENHANCED_GENERAL_PURPOSE_TYPE      = "g6e"
+	ALIBABA_SYSTEMDISK_CLOUD_ESSD_CATEGORY     = "cloud_essd"
+)
+
+// Why predefined and dependency on code? Can be converted to API call - https://www.alibabacloud.com/help/en/elastic-compute-service/latest/regions-describeregions
+var alibabaRegions = []string{
+	"cn-qingdao",
+	"cn-beijing",
+	"cn-zhangjiakou",
+	"cn-huhehaote",
+	"cn-wulanchabu",
+	"cn-hangzhou",
+	"cn-shanghai",
+	"cn-nanjing",
+	"cn-fuzhou",
+	"cn-shenzhen",
+	"cn-guangzhou",
+	"cn-chengdu",
+	"cn-hongkong",
+	"ap-southeast-1",
+	"ap-southeast-2",
+	"ap-southeast-3",
+	"ap-southeast-5",
+	"ap-southeast-6",
+	"ap-southeast-7",
+	"ap-south-1",
+	"ap-northeast-1",
+	"ap-northeast-2",
+	"us-west-1",
+	"us-east-1",
+	"eu-central-1",
+	"me-east-1",
+}
+
+// To-Do: Convert to API call - https://www.alibabacloud.com/help/en/elastic-compute-service/latest/describeinstancetypefamilies
+// Also first pass only completely tested pricing API for General pupose instances families.
+var alibabaInstanceFamilies = []string{
+	"g6e",
+	"g6",
+	"g5",
+	"sn2",
+	"sn2ne",
+}
+
+// AlibabaAccessKey holds Alibaba credentials parsing from the service-key.json file.
+type AlibabaAccessKey struct {
+	AccessKeyID     string `json:"alibaba_access_key_id"`
+	SecretAccessKey string `json:"alibaba_secret_access_key"`
+}
+
+// TO-DO: Slim Version of k8s disk assigned to a node, To be used if price adjustment need to happen with local disk information passed to describePrice.
+type SlimK8sDisk struct {
+	DiskType         string
+	RegionID         string
+	DiskCategory     string
+	PerformanceLevel string
+	PriceUnit        string
+	SizeInGiB        int32
+	ProviderID       string
+}
+
+// Slim version of a k8s v1.node just to pass along the object of this struct instead of constant getting the labels from within v1.Node & unit testing.
+type SlimK8sNode struct {
+	InstanceType       string
+	RegionID           string
+	PriceUnit          string
+	MemorySizeInKiB    string // TO-DO : Possible to convert to float?
+	IsIoOptimized      bool
+	OSType             string
+	ProviderID         string
+	InstanceTypeFamily string // Bug in DescribePrice, doesn't default to enhanced type correct and you get an error in DescribePrice to get around need the family of the InstanceType.
+}
+
+func NewSlimK8sNode(instanceType, regionID, priceUnit, memorySizeInKiB, osType, providerID, instanceTypeFamily string, isIOOptimized bool) *SlimK8sNode {
+	return &SlimK8sNode{
+		InstanceType:       instanceType,
+		RegionID:           regionID,
+		PriceUnit:          priceUnit,
+		MemorySizeInKiB:    memorySizeInKiB,
+		IsIoOptimized:      isIOOptimized,
+		OSType:             osType,
+		ProviderID:         providerID,
+		InstanceTypeFamily: instanceTypeFamily,
+	}
+}
+
+// AlibabaNodeAttributes represents metadata about the product used to map to a node.
+// Basic Attributes needed atleast to get the key, Some attributes from k8s Node response
+// be populated directly into *Node object.
+type AlibabaNodeAttributes struct {
+	InstanceType    string `json:"instanceType"`
+	MemorySizeInKiB string `json:"memorySizeInKiB"`
+	IsIoOptimized   bool   `json:"isIoOptimized"`
+	OSType          string `json:"osType"`
+}
+
+func NewAlibabaNodeAttributes(node *SlimK8sNode) *AlibabaNodeAttributes {
+	return &AlibabaNodeAttributes{
+		InstanceType:    node.InstanceType,
+		MemorySizeInKiB: node.MemorySizeInKiB,
+		IsIoOptimized:   node.IsIoOptimized,
+		OSType:          node.OSType,
+	}
+}
+
+// AlibabaPVAttributes represents metadata about the product used to map to a PV.
+// Basic Attributes needed atleast to get the keys, Some attributes from k8s Node response
+// be populated directly into *PV object.
+// TO_DO: In next PR improve this
+type AlibabaPVAttributes struct {
+	DiskType         int32  `json:"diskType"`
+	DiskCategory     string `json:"diskCategory"`
+	PerformanceLevel string `json:"performanceLevel"`
+}
+
+// Stage 1 support will be Pay-As-You-Go with HourlyPrice equal to TradePrice with PriceUnit as Hour
+// TO-DO: Subscription and Premptible support, need to find how to distinguish node into these categories]
+// TO-DO: Open question Subscription would be either Monthly or Yearly, Firstly Data retrieval/population
+// TO-DO:  need to be tested from describe price API, but how would you calculate hourly price, is it PRICE_YEARLY/HOURS_IN_THE_YEAR?
+type AlibabaPricingDetails struct {
+	// Represents hourly price for the given Alibaba cloud Product.
+	HourlyPrice float32 `json:"hourlyPrice"`
+	// Represents the unit in which Alibaba Product is billed can be Hour, Month or Year based on the billingMethod.
+	PriceUnit string `json:"priceUnit"`
+	// Original Price paid to acquire the Alibaba Product.
+	TradePrice float32 `json:"tradePrice"`
+	// Represents the currency unit of the price for billing Alibaba Product.
+	CurrencyCode string `json:"currencyCode"`
+}
+
+func NewAlibabaPricingDetails(hourlyPrice float32, priceUnit string, tradePrice float32, currencyCode string) *AlibabaPricingDetails {
+	return &AlibabaPricingDetails{
+		HourlyPrice:  hourlyPrice,
+		PriceUnit:    priceUnit,
+		TradePrice:   tradePrice,
+		CurrencyCode: currencyCode,
+	}
+}
+
+// AlibabaPricingTerms can have three types of supported billing method Pay-As-You-Go, Subscription and Premptible
+type AlibabaPricingTerms struct {
+	BillingMethod  string                 `json:"billingMethod"`
+	PricingDetails *AlibabaPricingDetails `json:"pricingDetails"`
+}
+
+func NewAlibabaPricingTerms(billingMethod string, pricingDetails *AlibabaPricingDetails) *AlibabaPricingTerms {
+	return &AlibabaPricingTerms{
+		BillingMethod:  billingMethod,
+		PricingDetails: pricingDetails,
+	}
+}
+
+// Alibaba Pricing struct carry the Attributes and pricing information for Node or PV
+type AlibabaPricing struct {
+	NodeAttributes *AlibabaNodeAttributes
+	PVAttributes   *AlibabaPVAttributes
+	PricingTerms   *AlibabaPricingTerms
+	Node           *Node
+	PV             *PV
+}
+
+// Alibaba cloud's Provider struct
+type Alibaba struct {
+	// Data to store Alibaba cloud's pricing struct, key in the map represents exact match to
+	// node.features() or pv.features for easy lookup
+	Pricing map[string]*AlibabaPricing
+	// Lock Needed to provide thread safe
+	DownloadPricingDataLock sync.RWMutex
+	Clientset               clustercache.ClusterCache
+	Config                  *ProviderConfig
+	*CustomProvider
+
+	// TO-DO: These needs to be decided if either exported or unexported.
+	serviceAccountChecks *ServiceAccountChecks
+	clusterAccountId     string
+	clusterRegion        string
+
+	// The following fields are unexported because of avoiding any leak of secrets of these keys.
+	// Alibaba Access key used specifically in signer interface used to sign API calls
+	accessKey *credentials.AccessKeyCredential
+	// Map of regionID to sdk.client to call API for that region
+	clients map[string]*sdk.Client
+}
+
+// GetAlibabaAccessKey return the Access Key used to interact with the Alibaba cloud, if not set it
+// set it first by looking at env variables else load it from secret files.
+// <IMPORTANT>Ask in PR what is the exact purpose of so many functions to set the key in AWS providers, am i missing something here!!!!!
+func (alibaba *Alibaba) GetAlibabaAccessKey() (*credentials.AccessKeyCredential, error) {
+	if alibaba.accessKeyisLoaded() {
+		return alibaba.accessKey, nil
+	}
+
+	config, err := alibaba.GetConfig()
+	if err != nil {
+		return nil, fmt.Errorf("error getting the default config for Alibaba Cloud provider: %w", err)
+	}
+
+	//Look for service key values in env if not present in config via helm chart once changes are done
+	if config.AlibabaServiceKeyName == "" {
+		config.AlibabaServiceKeyName = env.GetAlibabaAccessKeyID()
+	}
+	if config.AlibabaServiceKeySecret == "" {
+		config.AlibabaServiceKeySecret = env.GetAlibabaAccessKeySecret()
+	}
+
+	if config.AlibabaServiceKeyName == "" && config.AlibabaServiceKeySecret == "" {
+		log.Debugf("missing service key values for Alibaba cloud integration attempting to use service account integration")
+		err := alibaba.loadAlibabaAuthSecretAndSetEnv(true)
+		if err != nil {
+			return nil, fmt.Errorf("unable to set the Alibaba Cloud key/secret from config file %w", err)
+		}
+		// set custom pricing keys too
+		config.AlibabaServiceKeyName = env.GetAlibabaAccessKeyID()
+		config.AlibabaServiceKeySecret = env.GetAlibabaAccessKeySecret()
+	}
+
+	if config.AlibabaServiceKeyName == "" && config.AlibabaServiceKeySecret == "" {
+		return nil, fmt.Errorf("failed to get the access key for the current alibaba account")
+	}
+
+	alibaba.accessKey = &credentials.AccessKeyCredential{AccessKeyId: env.GetAlibabaAccessKeyID(), AccessKeySecret: env.GetAlibabaAccessKeySecret()}
+
+	return alibaba.accessKey, nil
+}
+
+func (alibaba *Alibaba) DownloadPricingData() error {
+	alibaba.DownloadPricingDataLock.Lock()
+	defer alibaba.DownloadPricingDataLock.Unlock()
+
+	var aak *credentials.AccessKeyCredential
+	var err error
+
+	if !alibaba.accessKeyisLoaded() {
+		aak, err = alibaba.GetAlibabaAccessKey()
+		if err != nil {
+			return fmt.Errorf("unable to get the access key information: %w", err)
+		}
+	} else {
+		aak = alibaba.accessKey
+	}
+
+	c, err := alibaba.Config.GetCustomPricingData()
+	if err != nil {
+		return fmt.Errorf("error downloading default pricing data: %w", err)
+	}
+
+	// Get all the nodes from Alibaba cluster.
+	nodeList := alibaba.Clientset.GetAllNodes()
+
+	var client *sdk.Client
+	var signer *signers.AccessKeySigner
+	var ok bool
+	var pricingObj *AlibabaPricing
+	var lookupKey string
+	alibaba.clients = make(map[string]*sdk.Client)
+	alibaba.Pricing = make(map[string]*AlibabaPricing)
+
+	// TO-DO: Add disk price adjustment by parsing the local disk information and putting it as a param in describe Price function.
+	for _, node := range nodeList {
+		slimK8sNode := generateSlimK8sNodeFromV1Node(node)
+		lookupKey, err = determineKeyForPricing(slimK8sNode)
+		if _, ok := alibaba.Pricing[lookupKey]; ok {
+			log.Debugf("Pricing information for node with same features %s already exists hence skipping", lookupKey)
+			continue
+		}
+
+		if client, ok = alibaba.clients[slimK8sNode.RegionID]; !ok {
+			client, err = sdk.NewClientWithAccessKey(slimK8sNode.RegionID, aak.AccessKeyId, aak.AccessKeySecret)
+			if err != nil {
+				return fmt.Errorf("unable to initiate alibaba cloud sdk client for region %s : %w", slimK8sNode.RegionID, err)
+			}
+			alibaba.clients[slimK8sNode.RegionID] = client
+		}
+		signer = signers.NewAccessKeySigner(aak)
+		pricingObj, err = processDescribePriceAndCreateAlibabaPricing(client, slimK8sNode, signer, c)
+
+		if err != nil {
+			return fmt.Errorf("failed to create pricing information for node with type %s with error: %w", slimK8sNode.InstanceType, err)
+		}
+		alibaba.Pricing[lookupKey] = pricingObj
+	}
+
+	// TO-DO: PV pricing
+	// //get pvList ultimately from Alibaba cloud provider and resemble data from the pvtype to
+	// // Hardcodedk8sNodeDiskStruct
+	// pvList := alibaba.Clientset.GetAllPersistentVolumes()
+
+	// pvList := []*Hardcodedk8sNodeDiskStruct{}
+	// pvList = append(pvList, &Hardcodedk8sNodeDiskStruct{
+	// 	DiskType:         "data",
+	// 	DiskCategory:     "cloud",
+	// 	PerformanceLevel: "",
+	// 	RegionID:         "cn-hangzhou",
+	// 	PriceUnit:        "Hour",
+	// 	SizeInGiB:        60,
+	// 	ProviderID:       "Ali-XXX-pv-01",
+	// }, &Hardcodedk8sNodeDiskStruct{
+	// 	DiskType:         "data",
+	// 	DiskCategory:     "cloud",
+	// 	PerformanceLevel: "P1",
+	// 	RegionID:         "cn-hangzhou",
+	// 	PriceUnit:        "Hour",
+	// 	SizeInGiB:        40,
+	// 	ProviderID:       "Ali-XXX-pv-01",
+	// })
+
+	// for _, pv := range pvList {
+	// 	if client, ok = alibaba.clients[pv.RegionID]; !ok {
+	// 		client, err = sdk.NewClientWithAccessKey(pv.RegionID, aak.AccessKeyId, aak.AccessKeySecret)
+	// 		if err != nil {
+	// 			return fmt.Errorf("access key provided does not have access to location %s", pv.RegionID)
+	// 		}
+	// 		alibaba.clients[pv.RegionID] = client
+	// 	}
+	// 	signer = signers.NewAccessKeySigner(aak)
+	// 	pricingObj, err = processDescribePriceAndCreateAlibabaPricing(client, pv, signer)
+	// 	lookupKey, err = determineKeyForPricing(pv)
+	// 	if err != nil {
+	// 		return err
+	// 	}
+	// 	alibaba.Pricing[lookupKey] = pricingObj
+	// }
+	// log.Infof("Length of pricing is %d", len(alibaba.Pricing))
+	// log.Infof("random value is %v", alibaba.Pricing[lookupKey])
+	return nil
+}
+
+// AllNodePricing returns all the billing data for nodes and pvs
+func (alibaba *Alibaba) AllNodePricing() (interface{}, error) {
+	alibaba.DownloadPricingDataLock.RLock()
+	defer alibaba.DownloadPricingDataLock.RUnlock()
+	return alibaba.Pricing, nil
+}
+
+// NodePricing gives a specific node for the key
+func (alibaba *Alibaba) NodePricing(key Key) (*Node, error) {
+	alibaba.DownloadPricingDataLock.RLock()
+	defer alibaba.DownloadPricingDataLock.RUnlock()
+
+	// Get node features for the key
+	keyFeature := key.Features()
+
+	pricing, ok := alibaba.Pricing[keyFeature]
+	if !ok {
+		log.Warnf("Node pricing information not found for node with feature: %s", keyFeature)
+		return &Node{}, nil
+	}
+
+	log.Debugf("returning the node price for the node with feature: %s", keyFeature)
+	return pricing.Node, nil
+}
+
+// PVPricing gives a specific PV price for the PVkey
+func (alibaba *Alibaba) PVPricing(pvk PVKey) (*PV, error) {
+	alibaba.DownloadPricingDataLock.RLock()
+	defer alibaba.DownloadPricingDataLock.RUnlock()
+
+	keyFeature := pvk.Features()
+
+	pricing, ok := alibaba.Pricing[keyFeature]
+
+	if !ok {
+		log.Warnf("Persistent Volume pricing not found for PV with feature: %s", keyFeature)
+		return &PV{}, nil
+	}
+
+	log.Debugf("returning the PV price for the node with feature: %s", keyFeature)
+	return pricing.PV, nil
+}
+
+// Stubbed NetworkPricing for Alibaba Cloud. Will look at this in Next PR
+func (alibaba *Alibaba) NetworkPricing() (*Network, error) {
+	return &Network{
+		ZoneNetworkEgressCost:     0.0,
+		RegionNetworkEgressCost:   0.0,
+		InternetNetworkEgressCost: 0.0,
+	}, nil
+}
+
+// Stubbed LoadBalancerPricing for Alibaba Cloud. Will look at this in Next PR
+func (alibaba *Alibaba) LoadBalancerPricing() (*LoadBalancer, error) {
+	return &LoadBalancer{
+		Cost: 0.0,
+	}, nil
+}
+
+func (alibaba *Alibaba) GetConfig() (*CustomPricing, error) {
+	c, err := alibaba.Config.GetCustomPricingData()
+	if err != nil {
+		return nil, err
+	}
+	if c.Discount == "" {
+		c.Discount = "0%"
+	}
+	if c.NegotiatedDiscount == "" {
+		c.NegotiatedDiscount = "0%"
+	}
+	if c.ShareTenancyCosts == "" {
+		c.ShareTenancyCosts = defaultShareTenancyCost
+	}
+
+	return c, nil
+}
+
+// Load once and cache the result (even on failure). This is an install time secret, so
+// we don't expect the secret to change. If it does, however, we can force reload using
+// the input parameter.
+func (alibaba *Alibaba) loadAlibabaAuthSecretAndSetEnv(force bool) error {
+	if !force && alibaba.accessKeyisLoaded() {
+		return nil
+	}
+
+	exists, err := fileutil.FileExists(authSecretPath)
+	if !exists || err != nil {
+		return fmt.Errorf("failed to locate service account file: %s with err: %w", authSecretPath, err)
+	}
+
+	result, err := ioutil.ReadFile(authSecretPath)
+	if err != nil {
+		return fmt.Errorf("failed to read service account file: %s with err: %w", authSecretPath, err)
+	}
+
+	var ak *AlibabaAccessKey
+	err = json.Unmarshal(result, &ak)
+	if err != nil {
+		return fmt.Errorf("failed to unmarshall access key id and access key secret with err: %w", err)
+	}
+
+	err = env.Set(env.AlibabaAccessKeyIDEnvVar, ak.AccessKeyID)
+	if err != nil {
+		return fmt.Errorf("failed to set environment variable: %s with err: %w", env.AlibabaAccessKeyIDEnvVar, err)
+	}
+	err = env.Set(env.AlibabaAccessKeySecretEnvVar, ak.SecretAccessKey)
+	if err != nil {
+		return fmt.Errorf("failed to set environment variable: %s with err: %w", env.AlibabaAccessKeySecretEnvVar, err)
+	}
+
+	alibaba.accessKey = &credentials.AccessKeyCredential{
+		AccessKeyId:     ak.AccessKeyID,
+		AccessKeySecret: ak.SecretAccessKey,
+	}
+	return nil
+}
+
+// Regions returns a current supported list of Alibaba regions
+func (alibaba *Alibaba) Regions() []string {
+	return alibabaRegions
+}
+
+// ClusterInfo returns information about Alibaba Cloud cluster, as provided by metadata. TO-DO: Look at this function closely at next PR iteration
+func (alibaba *Alibaba) ClusterInfo() (map[string]string, error) {
+
+	c, err := alibaba.GetConfig()
+	if err != nil {
+		return nil, fmt.Errorf("failed to getConfig with err: %w", err)
+	}
+
+	var clusterName string
+	if c.ClusterName != "" {
+		clusterName = c.ClusterName
+	}
+
+	// Set it to environment clusterID if not set at this point
+	if clusterName == "" {
+		clusterName = env.GetClusterID()
+	}
+
+	m := make(map[string]string)
+	m["name"] = clusterName
+	m["provider"] = kubecost.AlibabaProvider
+	m["project"] = alibaba.clusterAccountId
+	m["region"] = alibaba.clusterRegion
+	m["id"] = env.GetClusterID()
+	return m, nil
+}
+
+// Will look at this in Next PR if needed
+func (alibaba *Alibaba) GetAddresses() ([]byte, error) {
+	return nil, nil
+}
+
+// Will look at this in Next PR if needed
+func (alibaba *Alibaba) GetDisks() ([]byte, error) {
+	return nil, nil
+}
+
+// Will look at this in Next PR if needed
+func (alibaba *Alibaba) UpdateConfig(r io.Reader, updateType string) (*CustomPricing, error) {
+	return nil, nil
+}
+
+// Will look at this in Next PR if needed
+func (alibaba *Alibaba) UpdateConfigFromConfigMap(cm map[string]string) (*CustomPricing, error) {
+	return nil, nil
+}
+
+// Will look at this in Next PR if needed
+func (alibaba *Alibaba) GetManagementPlatform() (string, error) {
+	return "", nil
+}
+
+// Will look at this in Next PR if needed
+func (alibaba *Alibaba) GetLocalStorageQuery(window, offset time.Duration, rate bool, used bool) string {
+	return ""
+}
+
+// Will look at this in Next PR if needed
+func (alibaba *Alibaba) ApplyReservedInstancePricing(nodes map[string]*Node) {
+
+}
+
+// Will look at this in Next PR if needed
+func (alibaba *Alibaba) ServiceAccountStatus() *ServiceAccountStatus {
+	return &ServiceAccountStatus{}
+}
+
+// Will look at this in Next PR if needed
+func (alibaba *Alibaba) PricingSourceStatus() map[string]*PricingSource {
+	return map[string]*PricingSource{}
+}
+
+// Will look at this in Next PR if needed
+func (alibaba *Alibaba) ClusterManagementPricing() (string, float64, error) {
+	return "", 0.0, nil
+}
+
+// Will look at this in Next PR if needed
+func (alibaba *Alibaba) CombinedDiscountForNode(string, bool, float64, float64) float64 {
+	return 0.0
+}
+
+func (alibaba *Alibaba) accessKeyisLoaded() bool {
+	return alibaba.accessKey != nil
+}
+
+type AlibabaNodeKey struct {
+	ProviderID       string
+	RegionID         string
+	InstanceType     string
+	OSType           string
+	OptimizedKeyword string //If IsIoOptimized key will have optimize if not unoptimized the key for the node
+}
+
+func NewAlibabaNodeKey(node *SlimK8sNode, optimizedKeyword string) *AlibabaNodeKey {
+	return &AlibabaNodeKey{
+		ProviderID:       node.ProviderID,
+		RegionID:         node.RegionID,
+		InstanceType:     node.InstanceType,
+		OSType:           node.OSType,
+		OptimizedKeyword: optimizedKeyword,
+	}
+}
+
+func (alibabaNodeKey *AlibabaNodeKey) ID() string {
+	return alibabaNodeKey.ProviderID
+}
+
+func (alibabaNodeKey *AlibabaNodeKey) Features() string {
+	keyLookup := stringutil.DeleteEmptyStringsFromArray([]string{alibabaNodeKey.RegionID, alibabaNodeKey.InstanceType, alibabaNodeKey.OSType, alibabaNodeKey.OptimizedKeyword})
+	return strings.Join(keyLookup, "::")
+}
+
+func (alibabaNodeKey *AlibabaNodeKey) GPUType() string {
+	return ""
+}
+
+func (alibabaNodeKey *AlibabaNodeKey) GPUCount() int {
+	return 0
+}
+
+// Get's the key for the k8s node input
+func (alibaba *Alibaba) GetKey(mapValue map[string]string, node *v1.Node) Key {
+	//Mostly parse the Node object and get the ProviderID, region, InstanceType, OSType and OptimizedKeyword(In if block)
+	// Currently just hardcoding a Node but eventually need to Node object
+	slimK8sNode := generateSlimK8sNodeFromV1Node(node)
+
+	optimizedKeyword := ""
+	if slimK8sNode.IsIoOptimized {
+		optimizedKeyword = ALIBABA_OPTIMIZE_KEYWORD
+	} else {
+		optimizedKeyword = ALIBABA_NON_OPTIMIZE_KEYWORD
+	}
+	return NewAlibabaNodeKey(slimK8sNode, optimizedKeyword)
+}
+
+type AlibabaPVKey struct {
+	ProviderID       string
+	RegionID         string
+	DiskType         string
+	DiskCategory     string
+	PerformaceLevel  string
+	StorageClassName string
+}
+
+func (alibabaPVKey *AlibabaPVKey) Features() string {
+	keyLookup := stringutil.DeleteEmptyStringsFromArray([]string{alibabaPVKey.RegionID, alibabaPVKey.DiskType, alibabaPVKey.DiskCategory, alibabaPVKey.PerformaceLevel})
+	return strings.Join(keyLookup, "::")
+}
+
+func (alibabaPVKey *AlibabaPVKey) ID() string {
+	return alibabaPVKey.ProviderID
+}
+
+// Get storage class information for PV.
+func (alibabaPVKey *AlibabaPVKey) GetStorageClass() string {
+	return alibabaPVKey.StorageClassName
+}
+
+// Helper functions for alibabaprovider.go
+
+// createDescribePriceACSRequest creates the HTTP GET request for the required resources' Price information,
+// When supporting subscription and Premptible resources this HTTP call needs to be modified with PriceUnit information
+// When supporting different new type of instances like Compute Optimized, Memory Optimized etc make sure you add the instance type
+// in unit test and check if it works or not to create the ack request and processDescribePriceAndCreateAlibabaPricing function
+// else more paramters need to be pulled from kubernetes node response or gather infromation from elsewhere and function modified.
+// TO-DO: Add disk adjustments to the node , Test it out!
+func createDescribePriceACSRequest(i interface{}) (*requests.CommonRequest, error) {
+	request := requests.NewCommonRequest()
+	request.Method = requests.GET
+	request.Product = ALIBABA_ECS_PRODUCT_CODE
+	request.Domain = ALIBABA_ECS_DOMAIN
+	request.Version = ALIBABA_ECS_VERSION
+	request.Scheme = requests.HTTPS
+	request.ApiName = ALIBABA_DESCRIBE_PRICE_API_ACTION
+	switch i.(type) {
+	case *SlimK8sNode:
+		node := i.(*SlimK8sNode)
+		request.QueryParams["RegionId"] = node.RegionID
+		request.QueryParams["ResourceType"] = ALIBABA_INSTANCE_RESOURCE_TYPE
+		request.QueryParams["InstanceType"] = node.InstanceType
+		request.QueryParams["PriceUnit"] = node.PriceUnit
+		// For Enhanced General Purpose Type g6e SystemDisk.Category param doesn't default right,
+		// need it to be specifically assigned to "cloud_ssd" otherwise there's errors
+		if node.InstanceTypeFamily == ALIBABA_ENHANCED_GENERAL_PURPOSE_TYPE {
+			request.QueryParams["SystemDisk.Category"] = ALIBABA_SYSTEMDISK_CLOUD_ESSD_CATEGORY
+		}
+		request.TransToAcsRequest()
+		return request, nil
+	case *SlimK8sDisk:
+		disk := i.(*SlimK8sDisk)
+		request.QueryParams["RegionId"] = disk.RegionID
+		request.QueryParams["ResourceType"] = ALIBABA_DISK_RESOURCE_TYPE
+		request.QueryParams["DataDisk.1.Category"] = disk.DiskCategory
+		request.QueryParams["DataDisk.1.Size"] = fmt.Sprintf("%d", disk.SizeInGiB)
+		request.QueryParams["PriceUnit"] = disk.PriceUnit
+		request.TransToAcsRequest()
+		return request, nil
+	default:
+		return nil, fmt.Errorf("unsupported ECS type (%T) for DescribePrice at this time", i)
+	}
+}
+
+// determineKeyForPricing generate a unique key from SlimK8sNode object that is construct from v1.Node object.
+func determineKeyForPricing(i interface{}) (string, error) {
+	if i == nil {
+		return "", fmt.Errorf("nil component passed to determine key")
+	}
+	switch i.(type) {
+	case *SlimK8sNode:
+		node := i.(*SlimK8sNode)
+		if node.IsIoOptimized {
+			keyLookup := stringutil.DeleteEmptyStringsFromArray([]string{node.RegionID, node.InstanceType, node.OSType, ALIBABA_OPTIMIZE_KEYWORD})
+			return strings.Join(keyLookup, "::"), nil
+		} else {
+			keyLookup := stringutil.DeleteEmptyStringsFromArray([]string{node.RegionID, node.InstanceType, node.OSType, ALIBABA_NON_OPTIMIZE_KEYWORD})
+			return strings.Join(keyLookup, "::"), nil
+		}
+	case *SlimK8sDisk:
+		disk := i.(*SlimK8sDisk)
+		keyLookup := stringutil.DeleteEmptyStringsFromArray([]string{disk.RegionID, disk.DiskCategory, disk.DiskType, disk.PerformanceLevel})
+		return strings.Join(keyLookup, "::"), nil
+	default:
+		return "", fmt.Errorf("unsupported ECS type (%T) at this time", i)
+	}
+}
+
+// Below structs are used to unmarshal json response of Alibaba cloud's API DescribePrice
+type Price struct {
+	OriginalPrice             float32 `json:"OriginalPrice"`
+	ReservedInstanceHourPrice float32 `json:"ReservedInstanceHourPrice"`
+	DiscountPrice             float32 `json:"DiscountPrice"`
+	Currency                  string  `json:"Currency"`
+	TradePrice                float32 `json:"TradePrice"`
+}
+
+type PriceInfo struct {
+	Price Price `json:"Price"`
+}
+type DescribePriceResponse struct {
+	RequestId string    `json:"RequestId"`
+	PriceInfo PriceInfo `json:"PriceInfo"`
+}
+
+// processDescribePriceAndCreateAlibabaPricing processes the DescribePrice API and generates the pricing information for alibaba node resource.
+func processDescribePriceAndCreateAlibabaPricing(client *sdk.Client, i interface{}, signer *signers.AccessKeySigner, custom *CustomPricing) (pricing *AlibabaPricing, err error) {
+	pricing = &AlibabaPricing{}
+	var response DescribePriceResponse
+	if i == nil {
+		return nil, fmt.Errorf("nil component passed to process the pricing information")
+	}
+	switch i.(type) {
+	case *SlimK8sNode:
+		node := i.(*SlimK8sNode)
+		req, err := createDescribePriceACSRequest(node)
+		if err != nil {
+			return nil, err
+		}
+		resp, err := client.ProcessCommonRequestWithSigner(req, signer)
+		pricing.NodeAttributes = NewAlibabaNodeAttributes(node)
+		if err != nil || resp.GetHttpStatus() != 200 {
+			// Can be defaulted to some value here?
+			return nil, fmt.Errorf("unable to fetch information for node with InstanceType: %v", node.InstanceType)
+		} else {
+			// This is where population of Pricing happens
+			err = json.Unmarshal(resp.GetHttpContentBytes(), &response)
+			if err != nil {
+				return nil, fmt.Errorf("unable to unmarshall json response to custom struct with err: %w", err)
+			}
+			// TO-DO : Ask in PR How to get the defaults is it equal to AWS/GCP defaults? And what needs to be returned
+			pricing.Node = &Node{
+				Cost:         fmt.Sprintf("%f", response.PriceInfo.Price.TradePrice),
+				BaseCPUPrice: custom.CPU,
+				BaseRAMPrice: custom.RAM,
+				BaseGPUPrice: custom.GPU,
+			}
+			// TO-DO : Currently with Pay-As-You-go Offering TradePrice = HourlyPrice , When support happens to other type HourlyPrice Need to be determined.
+			pricing.PricingTerms = NewAlibabaPricingTerms(ALIBABA_PAY_AS_YOU_GO_BILLING, NewAlibabaPricingDetails(response.PriceInfo.Price.TradePrice, ALIBABA_HOUR_PRICE_UNIT, response.PriceInfo.Price.TradePrice, response.PriceInfo.Price.Currency))
+		}
+	case *SlimK8sDisk:
+		disk := i.(*SlimK8sDisk)
+		req, err := createDescribePriceACSRequest(disk)
+		if err != nil {
+			return nil, err
+		}
+		resp, err := client.ProcessCommonRequestWithSigner(req, signer)
+		if err != nil {
+			return nil, fmt.Errorf("unable to fetch information for disk with DiskType: %v", disk.DiskType)
+		} else {
+			// This is where population of Pricing happens
+			err = json.Unmarshal(resp.GetHttpContentBytes(), &response)
+			if err != nil {
+				return nil, fmt.Errorf("unable to unmarshall json response to custom struct with err: %w", err)
+			}
+			pricing.PVAttributes = &AlibabaPVAttributes{}
+			pricing.PV = &PV{
+				Cost: fmt.Sprintf("%f", response.PriceInfo.Price.TradePrice),
+			}
+
+		}
+	default:
+		return nil, fmt.Errorf("unsupported ECS Pricing component of type (%T) at this time", i)
+	}
+
+	return pricing, nil
+}
+
+// This function is to get the InstanceFamily from the InstanceType , convention followed in
+// instance type is ecs.[FamilyName].[DifferentSize], it gets the familyName , if it is unable to get it
+// it lists the instance family name as Unknown.
+// TO-DO: might need predefined list of instance types.
+func getInstanceFamilyFromType(instanceType string) string {
+	splitinstanceType := strings.Split(instanceType, ".")
+	if len(splitinstanceType) != 3 {
+		log.Warnf("unable to find the family of the instance type %s, returning it's family type unknown", instanceType)
+		return ALIBABA_UNKNOWN_INSTANCE_FAMILY_TYPE
+	}
+	if !slices.Contains(alibabaInstanceFamilies, splitinstanceType[1]) {
+		log.Warnf("currently the instance family type %s is not valid or not tested completely for pricing API", instanceType)
+		return ALIBABA_NOT_SUPPORTED_INSTANCE_FAMILY_TYPE
+	}
+	return splitinstanceType[1]
+}
+
+// function geenerates SlimK8sNode from v1.Node for better passing slimmed struct between functions
+func generateSlimK8sNodeFromV1Node(node *v1.Node) *SlimK8sNode {
+	var regionID, osType, instanceType, providerID, priceUnit, instanceFamily string
+	var memorySizeInKiB string // TO-DO: try to convert it into float
+	var ok, IsIoOptimized bool
+	if regionID, ok = node.Labels["topology.kubernetes.io/region"]; !ok {
+		// HIGHLY UNLIKELY THAT THIS LABEL WONT BE THERE.
+		log.Debugf("No RegionID label for the node: %s", node.Name)
+	}
+	if osType, ok = node.Labels["beta.kubernetes.io/os"]; !ok {
+		// HIGHLY UNLIKELY THAT THIS LABEL WONT BE THERE.
+		log.Debugf("OS type undetected for the node: %s", node.Name)
+	}
+	if instanceType, ok = node.Labels["node.kubernetes.io/instance-type"]; !ok {
+		// HIGHLY UNLIKELY THAT THIS LABEL WONT BE THERE.
+		log.Debugf("Instance Type undetected for the node: %s", node.Name)
+	}
+
+	instanceFamily = getInstanceFamilyFromType(instanceType)
+	memorySizeInKiB = fmt.Sprintf("%s", node.Status.Capacity.Memory())
+	providerID = node.Spec.ProviderID // Alibaba Cloud provider doesnt follow convention of prefix with cloud provider name
+
+	// Looking at current Instance offering , all of the Instances seem to be I/O optimized - https://www.alibabacloud.com/help/en/elastic-compute-service/latest/instance-family
+	// Basic price Json has it as part of the key so defaulting to true.
+	IsIoOptimized = true
+	priceUnit = ALIBABA_HOUR_PRICE_UNIT
+
+	return NewSlimK8sNode(instanceType, regionID, priceUnit, memorySizeInKiB, osType, providerID, instanceFamily, IsIoOptimized)
+}

+ 294 - 0
pkg/cloud/aliyunprovider_test.go

@@ -0,0 +1,294 @@
+package cloud
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers"
+	v1 "k8s.io/api/core/v1"
+	resource "k8s.io/apimachinery/pkg/api/resource"
+)
+
+func TestCreateDescribePriceACSRequest(t *testing.T) {
+	node := &SlimK8sNode{
+		InstanceType:       "ecs.g6.large",
+		RegionID:           "cn-hangzhou",
+		PriceUnit:          "Hour",
+		MemorySizeInKiB:    "16KiB",
+		IsIoOptimized:      true,
+		OSType:             "Linux",
+		ProviderID:         "Ali-XXX-node-01",
+		InstanceTypeFamily: "g6",
+	}
+	_, err := createDescribePriceACSRequest(node)
+	if err != nil {
+		t.Errorf("Error converting to Alibaba cloud request")
+	}
+}
+
+func TestProcessDescribePriceAndCreateAlibabaPricing(t *testing.T) {
+	// Skipping this test case since it exposes secret but a good test case to verify when
+	// supporting a new family of instances, steps to perform are
+	// STEP 1: Comment the t.Skip() line and then replace XXX_KEY_ID with the alibaba key id of your account and XXX_SECRET_ID with alibaba cloud secret of your account.
+	// STEP 2: Once you verify describePrice is working and no change needed in processDescribePriceAndCreateAlibabaPricing, you can go ahead and revert the step 1 changes.
+
+	// This test case was use to test all general puprose instances
+
+	t.Skip()
+
+	client, err := sdk.NewClientWithAccessKey("cn-hangzhou", "XXX_KEY_ID", "XXX_SECRET_ID")
+	if err != nil {
+		t.Errorf("Error connecting to the Alibaba cloud")
+	}
+	aak := credentials.NewAccessKeyCredential("XXX_KEY_ID", "XXX_SECRET_ID")
+	signer := signers.NewAccessKeySigner(aak)
+
+	cases := []struct {
+		name          string
+		testNode      *SlimK8sNode
+		expectedError error
+	}{
+		{
+			name: "test Enhanced General Purpose Type g6e instance family",
+			testNode: &SlimK8sNode{
+				InstanceType:       "ecs.g6e.xlarge",
+				RegionID:           "cn-hangzhou",
+				PriceUnit:          "Hour",
+				MemorySizeInKiB:    "16777216KiB",
+				IsIoOptimized:      true,
+				OSType:             "Linux",
+				ProviderID:         "cn-hangzhou.i-test-01",
+				InstanceTypeFamily: "g6e",
+			},
+			expectedError: nil,
+		},
+		{
+			name: "test General Purpose Type g6 instance family",
+			testNode: &SlimK8sNode{
+				InstanceType:       "ecs.g6.3xlarge",
+				RegionID:           "cn-hangzhou",
+				PriceUnit:          "Hour",
+				MemorySizeInKiB:    "50331648KiB",
+				IsIoOptimized:      true,
+				OSType:             "Linux",
+				ProviderID:         "cn-hangzhou.i-test-02",
+				InstanceTypeFamily: "g6",
+			},
+			expectedError: nil,
+		},
+		{
+			name: "test General Purpose Type g5 instance family",
+			testNode: &SlimK8sNode{
+				InstanceType:       "ecs.g5.2xlarge",
+				RegionID:           "cn-hangzhou",
+				PriceUnit:          "Hour",
+				MemorySizeInKiB:    "33554432KiB",
+				IsIoOptimized:      true,
+				OSType:             "Linux",
+				ProviderID:         "cn-hangzhou.i-test-03",
+				InstanceTypeFamily: "g5",
+			},
+			expectedError: nil,
+		},
+		{
+			name: "test General Purpose Type sn2 instance family",
+			testNode: &SlimK8sNode{
+				InstanceType:       "ecs.sn2.large",
+				RegionID:           "cn-hangzhou",
+				PriceUnit:          "Hour",
+				MemorySizeInKiB:    "16777216KiB",
+				IsIoOptimized:      true,
+				OSType:             "Linux",
+				ProviderID:         "cn-hangzhou.i-test-04",
+				InstanceTypeFamily: "sn2",
+			},
+			expectedError: nil,
+		},
+		{
+			name: "test General Purpose Type with Enhanced Network Performance sn2ne instance family",
+			testNode: &SlimK8sNode{
+				InstanceType:       "ecs.sn2ne.2xlarge",
+				RegionID:           "cn-hangzhou",
+				PriceUnit:          "Hour",
+				MemorySizeInKiB:    "33554432KiB",
+				IsIoOptimized:      true,
+				OSType:             "Linux",
+				ProviderID:         "cn-hangzhou.i-test-05",
+				InstanceTypeFamily: "sn2ne",
+			},
+			expectedError: nil,
+		},
+		{
+			name:          "test for a nil information",
+			testNode:      nil,
+			expectedError: fmt.Errorf("unsupported ECS pricing component at this time"),
+		},
+	}
+	custom := &CustomPricing{}
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			pricingObj, err := processDescribePriceAndCreateAlibabaPricing(client, c.testNode, signer, custom)
+			if err != nil && c.expectedError == nil {
+				t.Fatalf("Case name %s: got an error %s", c.name, err)
+			}
+			if pricingObj == nil {
+				t.Fatalf("Case name %s: got a nil pricing object", c.name)
+			}
+			t.Logf("Pricing Information gathered for instanceType %s is %v", c.name, pricingObj.PricingTerms.PricingDetails.TradePrice)
+		})
+	}
+}
+
+func TestGetInstanceFamilyFromType(t *testing.T) {
+	cases := []struct {
+		name                   string
+		instanceType           string
+		expectedInstanceFamily string
+	}{
+		{
+			name:                   "test if ecs.[instance-family].[different-type] work",
+			instanceType:           "ecs.sn2ne.2xlarge",
+			expectedInstanceFamily: "sn2ne",
+		},
+		{
+			name:                   "test if random word gives you ALIBABA_UNKNOWN_INSTANCE_FAMILY_TYPE value ",
+			instanceType:           "random.value",
+			expectedInstanceFamily: ALIBABA_UNKNOWN_INSTANCE_FAMILY_TYPE,
+		},
+		{
+			name:                   "test if random instance family gives you ALIBABA_NOT_SUPPORTED_INSTANCE_FAMILY_TYPE value ",
+			instanceType:           "ecs.g7e.2xlarge",
+			expectedInstanceFamily: ALIBABA_NOT_SUPPORTED_INSTANCE_FAMILY_TYPE,
+		},
+	}
+
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			returnValue := getInstanceFamilyFromType(c.instanceType)
+			if returnValue != c.expectedInstanceFamily {
+				t.Fatalf("Case name %s: expected instance family of type %s but got %s", c.name, c.expectedInstanceFamily, returnValue)
+			}
+		})
+	}
+}
+
+func TestDetermineKeyForPricing(t *testing.T) {
+	type randomK8sStruct struct {
+		name string
+	}
+	cases := []struct {
+		name          string
+		testVar       interface{}
+		expectedKey   string
+		expectedError error
+	}{
+		{
+			name: "test when all RegionID, InstanceType, OSType & ALIBABA_OPTIMIZE_KEYWORD words are used to key",
+			testVar: &SlimK8sNode{
+				InstanceType:       "ecs.sn2.large",
+				RegionID:           "cn-hangzhou",
+				PriceUnit:          "Hour",
+				MemorySizeInKiB:    "16777216KiB",
+				IsIoOptimized:      true,
+				OSType:             "linux",
+				ProviderID:         "cn-hangzhou.i-test-04",
+				InstanceTypeFamily: "sn2",
+			},
+			expectedKey:   "cn-hangzhou::ecs.sn2.large::linux::optimize",
+			expectedError: nil,
+		},
+		{
+			name: "test missing InstanceType to create key",
+			testVar: &SlimK8sNode{
+				RegionID:        "cn-hangzhou",
+				PriceUnit:       "Hour",
+				MemorySizeInKiB: "16777216KiB",
+				IsIoOptimized:   true,
+				OSType:          "linux",
+				ProviderID:      "cn-hangzhou.i-test-04",
+			},
+			expectedKey:   "cn-hangzhou::linux::optimize",
+			expectedError: nil,
+		},
+		{
+			name: "test random k8s struct should return unsupported error",
+			testVar: &randomK8sStruct{
+				name: "test struct",
+			},
+			expectedKey:   "",
+			expectedError: fmt.Errorf("unsupported ECS type randomK8sStruct for DescribePrice at this time"),
+		},
+		{
+			name:          "test for nil check",
+			testVar:       nil,
+			expectedKey:   "",
+			expectedError: fmt.Errorf("unsupported ECS type randomK8sStruct for DescribePrice at this time"),
+		},
+	}
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			returnString, returnErr := determineKeyForPricing(c.testVar)
+			if c.expectedError == nil && returnErr != nil {
+				t.Fatalf("Case name %s: expected error was nil but recieved error %v", c.name, returnErr)
+			}
+			if returnString != c.expectedKey {
+				t.Fatalf("Case name %s: determineKeyForPricing recieved %s but expected %s", c.name, returnString, c.expectedKey)
+			}
+		})
+	}
+}
+
+func TestGenerateSlimK8sNodeFromV1Node(t *testing.T) {
+	testv1Node := &v1.Node{}
+	testv1Node.Labels = make(map[string]string)
+	testv1Node.Labels["topology.kubernetes.io/region"] = "us-east-1"
+	testv1Node.Labels["beta.kubernetes.io/os"] = "linux"
+	testv1Node.Labels["node.kubernetes.io/instance-type"] = "ecs.sn2ne.2xlarge"
+	testv1Node.Status.Capacity = v1.ResourceList{
+		v1.ResourceMemory: *resource.NewQuantity(16, resource.BinarySI),
+	}
+	cases := []struct {
+		name             string
+		testNode         *v1.Node
+		expectedSlimNode *SlimK8sNode
+	}{
+		{
+			name:     "test a generic *v1.Node to *SlimK8sNode Conversion",
+			testNode: testv1Node,
+			expectedSlimNode: &SlimK8sNode{
+				InstanceType:       "ecs.sn2ne.2xlarge",
+				RegionID:           "us-east-1",
+				PriceUnit:          "Hour",
+				MemorySizeInKiB:    "16",
+				IsIoOptimized:      true,
+				OSType:             "linux",
+				InstanceTypeFamily: "sn2ne",
+			},
+		},
+	}
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			returnSlimK8sNode := generateSlimK8sNodeFromV1Node(c.testNode)
+			if returnSlimK8sNode.InstanceType != c.expectedSlimNode.InstanceType {
+				t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected InstanceType: %s , recieved Instance Type: %s", c.expectedSlimNode.InstanceType, returnSlimK8sNode.InstanceType)
+			}
+			if returnSlimK8sNode.RegionID != c.expectedSlimNode.RegionID {
+				t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected RegionID: %s , recieved RegionID Type: %s", c.expectedSlimNode.RegionID, returnSlimK8sNode.RegionID)
+			}
+			if returnSlimK8sNode.PriceUnit != c.expectedSlimNode.PriceUnit {
+				t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected PriceUnit: %s , recieved PriceUnit Type: %s", c.expectedSlimNode.PriceUnit, returnSlimK8sNode.PriceUnit)
+			}
+			if returnSlimK8sNode.MemorySizeInKiB != c.expectedSlimNode.MemorySizeInKiB {
+				t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected MemorySizeInKiB: %s , recieved MemorySizeInKiB Type: %s", c.expectedSlimNode.MemorySizeInKiB, returnSlimK8sNode.MemorySizeInKiB)
+			}
+			if returnSlimK8sNode.OSType != c.expectedSlimNode.OSType {
+				t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected OSType: %s , recieved OSType Type: %s", c.expectedSlimNode.OSType, returnSlimK8sNode.OSType)
+			}
+			if returnSlimK8sNode.InstanceTypeFamily != c.expectedSlimNode.InstanceTypeFamily {
+				t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected InstanceTypeFamily: %s , recieved InstanceTypeFamily Type: %s", c.expectedSlimNode.InstanceTypeFamily, returnSlimK8sNode.InstanceTypeFamily)
+			}
+		})
+	}
+}

+ 14 - 0
pkg/cloud/provider.go

@@ -161,6 +161,8 @@ type CustomPricing struct {
 	GpuLabelValue                string `json:"gpuLabelValue,omitempty"`
 	ServiceKeyName               string `json:"awsServiceKeyName,omitempty"`
 	ServiceKeySecret             string `json:"awsServiceKeySecret,omitempty"`
+	AlibabaServiceKeyName        string `json:"alibabaServiceKeyName,omitempty"`
+	AlibabaServiceKeySecret      string `json:"alibabaServiceKeySecret,omitempty"`
 	SpotDataRegion               string `json:"awsSpotDataRegion,omitempty"`
 	SpotDataBucket               string `json:"awsSpotDataBucket,omitempty"`
 	SpotDataPrefix               string `json:"awsSpotDataPrefix,omitempty"`
@@ -488,6 +490,15 @@ func NewProvider(cache clustercache.ClusterCache, apiKey string, config *config.
 			clusterAccountId:     cp.accountID,
 			serviceAccountChecks: NewServiceAccountChecks(),
 		}, nil
+	case kubecost.AlibabaProvider:
+		log.Info("Found ProviderID starting with \"alibaba\", using Alibaba Cloud Provider")
+		return &Alibaba{
+			Clientset:            cache,
+			Config:               NewProviderConfig(config, cp.configFileName),
+			clusterRegion:        cp.region,
+			clusterAccountId:     cp.accountID,
+			serviceAccountChecks: NewServiceAccountChecks(),
+		}, nil
 	case kubecost.ScalewayProvider:
 		log.Info("Found ProviderID starting with \"scaleway\", using Scaleway Provider")
 		return &Scaleway{
@@ -536,6 +547,9 @@ func getClusterProperties(node *v1.Node) clusterProperties {
 	} else if strings.HasPrefix(providerID, "scaleway") { // the scaleway provider ID looks like scaleway://instance/<instance_id>
 		cp.provider = kubecost.ScalewayProvider
 		cp.configFileName = "scaleway.json"
+	} else if strings.Contains(node.Status.NodeInfo.KubeletVersion, "aliyun") { // provider ID is not prefix with any distinct keyword like other providers
+		cp.provider = kubecost.AlibabaProvider
+		cp.configFileName = "alibaba.json"
 	}
 	if env.IsUseCSVProvider() {
 		cp.provider = kubecost.CSVProvider

+ 15 - 0
pkg/env/costmodelenv.go

@@ -16,6 +16,9 @@ const (
 	AWSAccessKeySecretEnvVar = "AWS_SECRET_ACCESS_KEY"
 	AWSClusterIDEnvVar       = "AWS_CLUSTER_ID"
 
+	AlibabaAccessKeyIDEnvVar     = "ALIBABA_ACCESS_KEY_ID"
+	AlibabaAccessKeySecretEnvVar = "ALIBABA_SECRET_ACCESS_KEY"
+
 	KubecostNamespaceEnvVar        = "KUBECOST_NAMESPACE"
 	PodNameEnvVar                  = "POD_NAME"
 	ClusterIDEnvVar                = "CLUSTER_ID"
@@ -206,6 +209,18 @@ func GetAWSClusterID() string {
 	return Get(AWSClusterIDEnvVar, "")
 }
 
+// GetAlibabaAccessKeyID returns the environment variable value for AlibabaAccessKeyIDEnvVar which represents
+// the Alibaba access key for authentication
+func GetAlibabaAccessKeyID() string {
+	return Get(AlibabaAccessKeyIDEnvVar, "")
+}
+
+// GetAlibabaAccessKeySecret returns the environment variable value for AlibabaAccessKeySecretEnvVar which represents
+// the Alibaba access key secret for authentication
+func GetAlibabaAccessKeySecret() string {
+	return Get(AlibabaAccessKeySecretEnvVar, "")
+}
+
 // GetKubecostNamespace returns the environment variable value for KubecostNamespaceEnvVar which
 // represents the namespace the cost model exists in.
 func GetKubecostNamespace() string {

+ 3 - 0
pkg/kubecost/assetprops.go

@@ -99,6 +99,9 @@ const GCPProvider = "GCP"
 // AzureProvider describes the provider Azure
 const AzureProvider = "Azure"
 
+// AlibabaProvider describes the provider for Alibaba Cloud
+const AlibabaProvider = "Alibaba"
+
 // CSVProvider describes the provider a CSV
 const CSVProvider = "CSV"
 

+ 10 - 0
pkg/util/stringutil/stringutil.go

@@ -164,3 +164,13 @@ func StringSlicesEqual(left, right []string) bool {
 	}
 	return true
 }
+
+// DeleteEmptyStringsFromArray removes the empty strings from an array.
+func DeleteEmptyStringsFromArray(input []string) (output []string) {
+	for _, str := range input {
+		if str != "" {
+			output = append(output, str)
+		}
+	}
+	return
+}