Skip to content

Commit 5bb23ef

Browse files
author
Joshua Reed
committed
Can switch users now.
1 parent 41c4b76 commit 5bb23ef

File tree

3 files changed

+78
-12
lines changed

3 files changed

+78
-12
lines changed

pkg/cloud/client.go

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,37 +35,38 @@ type Client interface {
3535
ZoneIFace
3636
IsoNetworkIface
3737
UserCredIFace
38+
NewClientFromSpec(Config) (Client, error)
3839
}
3940

4041
type client struct {
4142
cs *cloudstack.CloudStackClient
4243
csAsync *cloudstack.CloudStackClient
44+
config Config
4345
}
4446

4547
// cloud-config ini structure.
46-
type config struct {
48+
type Config struct {
4749
APIURL string `ini:"api-url"`
4850
APIKey string `ini:"api-key"`
4951
SecretKey string `ini:"secret-key"`
5052
VerifySSL bool `ini:"verify-ssl"`
5153
}
5254

5355
func NewClient(ccPath string) (Client, error) {
54-
c := &client{}
55-
cfg := &config{VerifySSL: true}
56+
c := &client{config: Config{VerifySSL: true}}
5657
if rawCfg, err := ini.Load(ccPath); err != nil {
5758
return nil, errors.Wrapf(err, "reading config at path %s:", ccPath)
5859
} else if g := rawCfg.Section("Global"); len(g.Keys()) == 0 {
5960
return nil, errors.New("section Global not found")
60-
} else if err = rawCfg.Section("Global").StrictMapTo(cfg); err != nil {
61+
} else if err = rawCfg.Section("Global").StrictMapTo(&c.config); err != nil {
6162
return nil, errors.Wrapf(err, "parsing [Global] section from config at path %s:", ccPath)
6263
}
6364

6465
// The client returned from NewAsyncClient works in a synchronous way. On the other hand,
6566
// a client returned from NewClient works in an asynchronous way. Dive into the constructor definition
6667
// comments for more details
67-
c.cs = cloudstack.NewAsyncClient(cfg.APIURL, cfg.APIKey, cfg.SecretKey, cfg.VerifySSL)
68-
c.csAsync = cloudstack.NewClient(cfg.APIURL, cfg.APIKey, cfg.SecretKey, cfg.VerifySSL)
68+
c.cs = cloudstack.NewAsyncClient(c.config.APIURL, c.config.APIKey, c.config.SecretKey, c.config.VerifySSL)
69+
c.csAsync = cloudstack.NewClient(c.config.APIURL, c.config.APIKey, c.config.SecretKey, c.config.VerifySSL)
6970

7071
_, err := c.cs.APIDiscovery.ListApis(c.cs.APIDiscovery.NewListApisParams())
7172
if err != nil && strings.Contains(strings.ToLower(err.Error()), "i/o timeout") {
@@ -74,6 +75,29 @@ func NewClient(ccPath string) (Client, error) {
7475
return c, errors.Wrap(err, "checking CloudStack API Client connectivity:")
7576
}
7677

78+
// NewClientFromSpec generates a new client from an existing client.
79+
// Unless the passed config contains a new API URL the original one will be used.
80+
// VerifySSL will be set to true if either the old or new configs is true.
81+
func (origC *client) NewClientFromSpec(cfg Config) (Client, error) {
82+
newC := &client{config: cfg}
83+
newC.config.VerifySSL = cfg.VerifySSL || origC.config.VerifySSL // Prefer the most secure setting given.
84+
if newC.config.APIURL == "" {
85+
newC.config.APIURL = origC.config.APIURL
86+
}
87+
88+
// The client returned from NewAsyncClient works in a synchronous way. On the other hand,
89+
// a client returned from NewClient works in an asynchronous way. Dive into the constructor definition
90+
// comments for more details
91+
newC.cs = cloudstack.NewAsyncClient(newC.config.APIURL, newC.config.APIKey, newC.config.SecretKey, newC.config.VerifySSL)
92+
newC.csAsync = cloudstack.NewClient(newC.config.APIURL, newC.config.APIKey, newC.config.SecretKey, newC.config.VerifySSL)
93+
94+
_, err := newC.cs.APIDiscovery.ListApis(newC.cs.APIDiscovery.NewListApisParams())
95+
if err != nil && strings.Contains(strings.ToLower(err.Error()), "i/o timeout") {
96+
return newC, errors.Wrap(err, "timeout while checking CloudStack API Client connectivity")
97+
}
98+
return newC, errors.Wrap(err, "checking CloudStack API Client connectivity:")
99+
}
100+
77101
func NewClientFromCSAPIClient(cs *cloudstack.CloudStackClient) Client {
78102
c := &client{cs: cs, csAsync: cs}
79103
return c

pkg/cloud/user_credentials.go

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type UserCredIFace interface {
2727
ResolveAccount(*Account) error
2828
ResolveUser(*User) error
2929
ResolveUserKeys(*User) error
30+
GetUserWithKeys(*User) (bool, error)
3031
}
3132

3233
// Domain contains specifications that identify a domain.
@@ -186,10 +187,10 @@ func (c *client) ResolveUserKeys(user *User) error {
186187

187188
// GetUserWithKeys will search a domain and account for the first user that has api keys.
188189
// Returns true if a user is found and false otherwise.
189-
func (c *client) GetUserWithKeys(user *User) (error, bool) {
190+
func (c *client) GetUserWithKeys(user *User) (bool, error) {
190191
// Resolve account prior to any user resolution activity.
191192
if err := c.ResolveAccount(&user.Account); err != nil {
192-
return errors.Wrap(err, "error encountered when resolving account details"), false
193+
return false, errors.Wrap(err, "error encountered when resolving account details")
193194
}
194195

195196
// List users and take first user that has already has api keys.
@@ -199,16 +200,43 @@ func (c *client) GetUserWithKeys(user *User) (error, bool) {
199200
p.SetListall(true)
200201
resp, err := c.cs.User.ListUsers(p)
201202
if err != nil {
202-
return err, false
203+
return false, err
203204
}
204205

205206
// Return first user with keys.
206207
for _, possibleUser := range resp.Users {
207208
user.ID = possibleUser.Id
208209
if err := c.ResolveUserKeys(user); err == nil {
209-
return nil, true
210+
return true, nil
210211
}
211212
}
212213
user.ID = ""
213-
return nil, false
214+
return false, nil
215+
}
216+
217+
// GetOrCreateUserWithKeys will search a domain and account for the first user that has api keys.
218+
// Creates a CAPC user if no such user is found.
219+
func (c *client) GetOrCreateUserWithKeys(user *User) error {
220+
return nil
221+
}
222+
223+
// CreateUserWithKeys will create a CloudStack user in the specified domain and account and generate API keyes for
224+
// said user.
225+
func (c *client) CreateUserWithKeys(user *User) error {
226+
return nil
227+
}
228+
229+
// SetupUserAPIKeys adds api keys to a specified user.
230+
func SetupUserAPIKeys() error {
231+
return nil
232+
}
233+
234+
// Create user creates a user in the specified account and domain.
235+
func (c *client) CreateUser(user *User) error {
236+
return nil
237+
}
238+
239+
// DeleteUser removes a user from CloudStack.
240+
func (c *client) DeleteUser(user *User) error {
241+
return nil
214242
}

pkg/cloud/user_credentials_test.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,21 @@ var _ = Describe("User Credentials", func() {
7777
})
7878

7979
It("can get an arbitrary user with keys from domain and account specifications alone", func() {
80-
Ω(client.ResolveUserKeys(&user)).Should(Succeed())
80+
found, err := client.GetUserWithKeys(&user)
81+
Ω(err).ShouldNot(HaveOccurred())
82+
Ω(found).Should(BeTrue())
83+
Ω(user.APIKey).ShouldNot(BeEmpty())
84+
})
85+
86+
It("can get create a new client as another user", func() {
87+
found, err := client.GetUserWithKeys(&user)
88+
Ω(err).ShouldNot(HaveOccurred())
89+
Ω(found).Should(BeTrue())
90+
Ω(user.APIKey).ShouldNot(BeEmpty())
91+
cfg := cloud.Config{APIKey: user.APIKey, SecretKey: user.SecretKey}
92+
newClient, err := client.NewClientFromSpec(cfg)
93+
Ω(err).ShouldNot(HaveOccurred())
94+
Ω(newClient).ShouldNot(BeNil())
8195
})
8296
})
8397
})

0 commit comments

Comments
 (0)