@@ -3,6 +3,7 @@ package cloud
33import (
44 "os"
55 "path/filepath"
6+ "strings"
67 "testing"
78)
89
@@ -79,3 +80,186 @@ func TestListCloudNames_NoFile(t *testing.T) {
7980 t .Error ("expected error when no clouds.yaml exists" )
8081 }
8182}
83+
84+ func TestCloudsYamlPaths_Order (t * testing.T ) {
85+ t .Setenv ("OS_CLIENT_CONFIG_FILE" , "/custom/path/clouds.yaml" )
86+ paths := CloudsYamlPaths ()
87+
88+ // First path should always be relative clouds.yaml
89+ if paths [0 ] != "clouds.yaml" {
90+ t .Errorf ("first path should be 'clouds.yaml', got %s" , paths [0 ])
91+ }
92+
93+ // Second path should be OS_CLIENT_CONFIG_FILE (when set)
94+ if paths [1 ] != "/custom/path/clouds.yaml" {
95+ t .Errorf ("second path should be OS_CLIENT_CONFIG_FILE, got %s" , paths [1 ])
96+ }
97+
98+ // Last path should be the system-wide path
99+ if paths [len (paths )- 1 ] != "/etc/openstack/clouds.yaml" {
100+ t .Errorf ("last path should be /etc/openstack/clouds.yaml, got %s" , paths [len (paths )- 1 ])
101+ }
102+ }
103+
104+ func TestCloudsYamlPaths_WithoutEnv (t * testing.T ) {
105+ // Ensure OS_CLIENT_CONFIG_FILE is not set
106+ t .Setenv ("OS_CLIENT_CONFIG_FILE" , "" )
107+ paths := CloudsYamlPaths ()
108+
109+ // Should have 3 paths: relative, home, system
110+ if len (paths ) != 3 {
111+ t .Errorf ("expected 3 paths without env, got %d: %v" , len (paths ), paths )
112+ }
113+
114+ if paths [0 ] != "clouds.yaml" {
115+ t .Errorf ("first path should be 'clouds.yaml', got %s" , paths [0 ])
116+ }
117+ }
118+
119+ func TestCloudsYamlPaths_WithEnv (t * testing.T ) {
120+ t .Setenv ("OS_CLIENT_CONFIG_FILE" , "/my/custom/clouds.yaml" )
121+ paths := CloudsYamlPaths ()
122+
123+ // Should have 4 paths: relative, env, home, system
124+ if len (paths ) != 4 {
125+ t .Errorf ("expected 4 paths with env, got %d: %v" , len (paths ), paths )
126+ }
127+
128+ if paths [1 ] != "/my/custom/clouds.yaml" {
129+ t .Errorf ("second path should be env path, got %s" , paths [1 ])
130+ }
131+ }
132+
133+ func TestListCloudNames_InvalidYAML (t * testing.T ) {
134+ dir := t .TempDir ()
135+ content := `clouds:
136+ bad:
137+ [invalid yaml {{{
138+ `
139+ path := filepath .Join (dir , "clouds.yaml" )
140+ if err := os .WriteFile (path , []byte (content ), 0644 ); err != nil {
141+ t .Fatal (err )
142+ }
143+
144+ t .Setenv ("OS_CLIENT_CONFIG_FILE" , path )
145+
146+ _ , err := ListCloudNames ()
147+ if err == nil {
148+ t .Error ("expected error for invalid YAML" )
149+ }
150+ if ! strings .Contains (err .Error (), "parsing" ) {
151+ t .Errorf ("error should mention parsing, got: %v" , err )
152+ }
153+ }
154+
155+ func TestListCloudNames_NoCloudsKey (t * testing.T ) {
156+ dir := t .TempDir ()
157+ content := `not_clouds:
158+ foo: bar
159+ `
160+ path := filepath .Join (dir , "clouds.yaml" )
161+ if err := os .WriteFile (path , []byte (content ), 0644 ); err != nil {
162+ t .Fatal (err )
163+ }
164+
165+ t .Setenv ("OS_CLIENT_CONFIG_FILE" , path )
166+
167+ names , err := ListCloudNames ()
168+ if err != nil {
169+ t .Fatalf ("unexpected error: %v" , err )
170+ }
171+ // Missing 'clouds' key means empty map, so 0 names
172+ if len (names ) != 0 {
173+ t .Errorf ("expected 0 clouds when 'clouds' key is missing, got %d" , len (names ))
174+ }
175+ }
176+
177+ func TestListCloudNames_EmptyFile (t * testing.T ) {
178+ dir := t .TempDir ()
179+ path := filepath .Join (dir , "clouds.yaml" )
180+ if err := os .WriteFile (path , []byte ("" ), 0644 ); err != nil {
181+ t .Fatal (err )
182+ }
183+
184+ t .Setenv ("OS_CLIENT_CONFIG_FILE" , path )
185+
186+ names , err := ListCloudNames ()
187+ if err != nil {
188+ t .Fatalf ("unexpected error: %v" , err )
189+ }
190+ if len (names ) != 0 {
191+ t .Errorf ("expected 0 clouds for empty file, got %d" , len (names ))
192+ }
193+ }
194+
195+ func TestListCloudNames_Precedence (t * testing.T ) {
196+ // When OS_CLIENT_CONFIG_FILE points to a valid file, it should be found
197+ // before the home path
198+ dir := t .TempDir ()
199+ content := `clouds:
200+ env_cloud:
201+ auth:
202+ auth_url: https://env.example.com:5000
203+ `
204+ path := filepath .Join (dir , "clouds.yaml" )
205+ if err := os .WriteFile (path , []byte (content ), 0644 ); err != nil {
206+ t .Fatal (err )
207+ }
208+
209+ t .Setenv ("OS_CLIENT_CONFIG_FILE" , path )
210+ t .Setenv ("HOME" , dir ) // ensure home path doesn't interfere
211+
212+ names , err := ListCloudNames ()
213+ if err != nil {
214+ t .Fatalf ("unexpected error: %v" , err )
215+ }
216+
217+ if len (names ) != 1 || names [0 ] != "env_cloud" {
218+ t .Errorf ("expected [env_cloud], got %v" , names )
219+ }
220+ }
221+
222+ func TestListCloudNames_DetailedCloudsYaml (t * testing.T ) {
223+ dir := t .TempDir ()
224+ content := `clouds:
225+ production:
226+ auth:
227+ auth_url: https://keystone.prod.example.com:5000
228+ username: admin
229+ password: secret
230+ project_name: ops
231+ user_domain_name: Default
232+ project_domain_name: Default
233+ region_name: RegionOne
234+ interface: public
235+ development:
236+ auth:
237+ auth_url: https://keystone.dev.example.com:5000/v3
238+ username: developer
239+ project_name: dev-team
240+ region_name: RegionOne
241+ identity_api_version: "3"
242+ `
243+ path := filepath .Join (dir , "clouds.yaml" )
244+ if err := os .WriteFile (path , []byte (content ), 0644 ); err != nil {
245+ t .Fatal (err )
246+ }
247+
248+ t .Setenv ("OS_CLIENT_CONFIG_FILE" , path )
249+
250+ names , err := ListCloudNames ()
251+ if err != nil {
252+ t .Fatalf ("unexpected error: %v" , err )
253+ }
254+
255+ if len (names ) != 2 {
256+ t .Fatalf ("expected 2 clouds, got %d" , len (names ))
257+ }
258+
259+ expected := []string {"development" , "production" }
260+ for i , name := range names {
261+ if name != expected [i ] {
262+ t .Errorf ("expected %s at index %d, got %s" , expected [i ], i , name )
263+ }
264+ }
265+ }
0 commit comments