Skip to content

Commit a5c5c4b

Browse files
authored
Merge pull request #284 from vishesh92/add-limits-check
Check available limits before deploying a VM
2 parents 88ec869 + fd7c081 commit a5c5c4b

12 files changed

+632
-90
lines changed

pkg/cloud/affinity_groups_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ var _ = Describe("AffinityGroup Unit Tests", func() {
4747
mockClient = cloudstack.NewMockClient(mockCtrl)
4848
ags = mockClient.AffinityGroup.(*cloudstack.MockAffinityGroupServiceIface)
4949
vms = mockClient.VirtualMachine.(*cloudstack.MockVirtualMachineServiceIface)
50-
client = cloud.NewClientFromCSAPIClient(mockClient)
50+
client = cloud.NewClientFromCSAPIClient(mockClient, nil)
5151
dummies.SetDummyVars()
5252
})
5353

pkg/cloud/client.go

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type client struct {
5959
cs *cloudstack.CloudStackClient
6060
csAsync *cloudstack.CloudStackClient
6161
config Config
62+
user *User
6263
customMetrics metrics.ACSCustomMetrics
6364
}
6465

@@ -73,6 +74,9 @@ type SecretConfig struct {
7374
var clientCache *ttlcache.Cache
7475
var cacheMutex sync.Mutex
7576

77+
var NewAsyncClient = cloudstack.NewAsyncClient
78+
var NewClient = cloudstack.NewClient
79+
7680
const ClientConfigMapName = "capc-client-config"
7781
const ClientConfigMapNamespace = "capc-system"
7882
const ClientCacheTTLKey = "client-cache-ttl"
@@ -168,9 +172,31 @@ func NewClientFromConf(conf Config, clientConfig *corev1.ConfigMap) (Client, err
168172
// a client returned from NewClient works in an asynchronous way. Dive into the constructor definition
169173
// comments for more details
170174
c := &client{config: conf}
171-
c.cs = cloudstack.NewAsyncClient(conf.APIUrl, conf.APIKey, conf.SecretKey, verifySSL)
172-
c.csAsync = cloudstack.NewClient(conf.APIUrl, conf.APIKey, conf.SecretKey, verifySSL)
175+
c.cs = NewClient(conf.APIUrl, conf.APIKey, conf.SecretKey, verifySSL)
176+
c.csAsync = NewAsyncClient(conf.APIUrl, conf.APIKey, conf.SecretKey, verifySSL)
173177
c.customMetrics = metrics.NewCustomMetrics()
178+
179+
p := c.cs.User.NewListUsersParams()
180+
userResponse, err := c.cs.User.ListUsers(p)
181+
if err != nil {
182+
return c, err
183+
}
184+
user := &User{
185+
ID: userResponse.Users[0].Id,
186+
Account: Account{
187+
Name: userResponse.Users[0].Account,
188+
Domain: Domain{
189+
Path: userResponse.Users[0].Domain,
190+
},
191+
},
192+
}
193+
if found, err := c.GetUserWithKeys(user); err != nil {
194+
return nil, err
195+
} else if !found {
196+
return nil, errors.Errorf(
197+
"could not find sufficient user (with API keys) in domain/account %s/%s", userResponse.Users[0].Domain, userResponse.Users[0].Account)
198+
}
199+
c.user = user
174200
clientCache.Set(clientCacheKey, c)
175201

176202
return c, nil
@@ -189,13 +215,33 @@ func (c *client) NewClientInDomainAndAccount(domain string, account string) (Cli
189215
}
190216
c.config.APIKey = user.APIKey
191217
c.config.SecretKey = user.SecretKey
218+
c.user = user
192219

193220
return NewClientFromConf(c.config, nil)
194221
}
195222

196-
// NewClientFromCSAPIClient creates a client from a CloudStack-Go API client. Mostly used for testing.
197-
func NewClientFromCSAPIClient(cs *cloudstack.CloudStackClient) Client {
198-
c := &client{cs: cs, csAsync: cs, customMetrics: metrics.NewCustomMetrics()}
223+
// NewClientFromCSAPIClient creates a client from a CloudStack-Go API client. Used only for testing.
224+
func NewClientFromCSAPIClient(cs *cloudstack.CloudStackClient, user *User) Client {
225+
if user == nil {
226+
user = &User{
227+
Account: Account{
228+
Domain: Domain{
229+
CPUAvailable: "Unlimited",
230+
MemoryAvailable: "Unlimited",
231+
VMAvailable: "Unlimited",
232+
},
233+
CPUAvailable: "Unlimited",
234+
MemoryAvailable: "Unlimited",
235+
VMAvailable: "Unlimited",
236+
},
237+
}
238+
}
239+
c := &client{
240+
cs: cs,
241+
csAsync: cs,
242+
customMetrics: metrics.NewCustomMetrics(),
243+
user: user,
244+
}
199245
return c
200246
}
201247

pkg/cloud/client_test.go

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ import (
2222

2323
corev1 "k8s.io/api/core/v1"
2424

25+
"github.com/apache/cloudstack-go/v2/cloudstack"
26+
"github.com/golang/mock/gomock"
2527
. "github.com/onsi/ginkgo/v2"
2628
. "github.com/onsi/gomega"
2729
"sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud"
30+
dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta1"
2831
"sigs.k8s.io/cluster-api-provider-cloudstack/test/helpers"
2932
)
3033

@@ -36,9 +39,20 @@ type Global struct {
3639

3740
var _ = Describe("Client", func() {
3841

39-
var ()
42+
var (
43+
mockCtrl *gomock.Controller
44+
mockClient *cloudstack.CloudStackClient
45+
us *cloudstack.MockUserServiceIface
46+
ds *cloudstack.MockDomainServiceIface
47+
as *cloudstack.MockAccountServiceIface
48+
)
4049

4150
BeforeEach(func() {
51+
mockCtrl = gomock.NewController(GinkgoT())
52+
mockClient = cloudstack.NewMockClient(mockCtrl)
53+
us = mockClient.User.(*cloudstack.MockUserServiceIface)
54+
ds = mockClient.Domain.(*cloudstack.MockDomainServiceIface)
55+
as = mockClient.Account.(*cloudstack.MockAccountServiceIface)
4256
})
4357

4458
AfterEach(func() {
@@ -100,10 +114,48 @@ var _ = Describe("Client", func() {
100114

101115
Context("NewClientFromConf", func() {
102116
clientConfig := &corev1.ConfigMap{}
117+
cloud.NewAsyncClient = func(apiurl, apikey, secret string, verifyssl bool, options ...cloudstack.ClientOption) *cloudstack.CloudStackClient {
118+
return mockClient
119+
}
120+
cloud.NewClient = func(apiurl, apikey, secret string, verifyssl bool, options ...cloudstack.ClientOption) *cloudstack.CloudStackClient {
121+
return mockClient
122+
}
103123

104124
BeforeEach(func() {
105125
clientConfig.Data = map[string]string{}
106126
clientConfig.Data[cloud.ClientCacheTTLKey] = "100ms"
127+
fakeListParams := &cloudstack.ListUsersParams{}
128+
fakeUser := &cloudstack.User{
129+
Id: dummies.UserID,
130+
Account: dummies.AccountName,
131+
Domain: dummies.DomainName,
132+
}
133+
us.EXPECT().NewListUsersParams().Return(fakeListParams).AnyTimes()
134+
us.EXPECT().ListUsers(fakeListParams).Return(&cloudstack.ListUsersResponse{
135+
Count: 1, Users: []*cloudstack.User{fakeUser},
136+
}, nil).AnyTimes()
137+
138+
dsp := &cloudstack.ListDomainsParams{}
139+
ds.EXPECT().NewListDomainsParams().Return(dsp).AnyTimes()
140+
ds.EXPECT().ListDomains(dsp).Return(&cloudstack.ListDomainsResponse{Count: 1, Domains: []*cloudstack.Domain{{
141+
Id: dummies.DomainID,
142+
Name: dummies.DomainName,
143+
Path: dummies.DomainPath,
144+
}}}, nil).AnyTimes()
145+
146+
asp := &cloudstack.ListAccountsParams{}
147+
as.EXPECT().NewListAccountsParams().Return(asp).AnyTimes()
148+
as.EXPECT().ListAccounts(asp).Return(&cloudstack.ListAccountsResponse{Count: 1, Accounts: []*cloudstack.Account{{
149+
Id: dummies.AccountID,
150+
Name: dummies.AccountName,
151+
}}}, nil).AnyTimes()
152+
ukp := &cloudstack.GetUserKeysParams{}
153+
us.EXPECT().NewGetUserKeysParams(gomock.Any()).Return(ukp).AnyTimes()
154+
us.EXPECT().GetUserKeys(ukp).Return(&cloudstack.GetUserKeysResponse{
155+
Apikey: dummies.Apikey,
156+
Secretkey: dummies.SecretKey,
157+
}, nil).AnyTimes()
158+
107159
})
108160

109161
It("Returns a new client", func() {
@@ -138,18 +190,5 @@ var _ = Describe("Client", func() {
138190
result2, _ := cloud.NewClientFromConf(config2, clientConfig)
139191
Ω(result1).Should(Equal(result2))
140192
})
141-
142-
It("Returns a new client after cache expiration", func() {
143-
config1 := cloud.Config{
144-
APIUrl: "http://5.5.5.5",
145-
}
146-
config2 := cloud.Config{
147-
APIUrl: "http://5.5.5.5",
148-
}
149-
result1, _ := cloud.NewClientFromConf(config1, clientConfig)
150-
time.Sleep(150 * time.Millisecond)
151-
result2, _ := cloud.NewClientFromConf(config2, clientConfig)
152-
Ω(result1).ShouldNot(Equal(result2))
153-
})
154193
})
155194
})

0 commit comments

Comments
 (0)