Skip to content

Commit b7236de

Browse files
Use gophercloud endpoint locator (#24)
1 parent 004985d commit b7236de

12 files changed

+94
-36
lines changed

cortex.secrets.example.yaml

-4
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,3 @@ conf:
3636
projectName: openstack-project-of-user
3737
userDomainName: openstack-domain-of-user
3838
projectDomainName: openstack-domain-of-project-scoped-to
39-
nova:
40-
url: https://path-to-nova/v2.1
41-
placement:
42-
url: https://path-to-placement

helm/cortex/Chart.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type: application
1515
# This is the chart version. This version number should be incremented each time you make changes
1616
# to the chart and its templates, including the app version.
1717
# Versions are expected to follow Semantic Versioning (https://semver.org/)
18-
version: 0.2.0
18+
version: 0.3.0
1919

2020
# This is the version number of the application being deployed. This version number should be
2121
# incremented each time you make changes to the application. Versions are not expected to

helm/cortex/values.yaml

+4-6
Original file line numberDiff line numberDiff line change
@@ -157,17 +157,15 @@ conf:
157157
userDomainName: openstack-domain-of-user
158158
projectDomainName: openstack-domain-of-project-scoped-to
159159
nova:
160-
# Compute service URL for openstack.
161-
url: https://path-to-nova/v2.1
162-
160+
# One of admin, public, or internal
161+
availability: public
163162
# Nova objects to sync into the database.
164163
types:
165164
- hypervisors
166165
- servers
167166
placement:
168-
# Placement service URL for openstack.
169-
url: https://path-to-placement
170-
167+
# One of admin, public, or internal
168+
availability: public
171169
# Placement objects to sync into the database.
172170
types:
173171
# - resource_providers

internal/conf/validation.go

+14-2
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,24 @@ func (c *config) Validate() error {
103103
// OpenStack urls should end without a slash.
104104
for _, url := range []string{
105105
c.SyncConfig.OpenStack.Keystone.URL,
106-
c.SyncConfig.OpenStack.Nova.URL,
107-
c.SyncConfig.OpenStack.Placement.URL,
108106
} {
109107
if strings.HasSuffix(url, "/") {
110108
return fmt.Errorf("openstack url %s should not end with a slash", url)
111109
}
112110
}
111+
// Check that the service availability is valid.
112+
validAvailabilities := []string{"public", "internal", "admin"}
113+
if c.SyncConfig.OpenStack.Nova.Availability == "" {
114+
c.SyncConfig.OpenStack.Nova.Availability = "public"
115+
}
116+
if c.SyncConfig.OpenStack.Placement.Availability == "" {
117+
c.SyncConfig.OpenStack.Placement.Availability = "public"
118+
}
119+
if !slices.Contains(validAvailabilities, c.SyncConfig.OpenStack.Nova.Availability) {
120+
return fmt.Errorf("invalid nova availability %s", c.SyncConfig.OpenStack.Nova.Availability)
121+
}
122+
if !slices.Contains(validAvailabilities, c.SyncConfig.OpenStack.Placement.Availability) {
123+
return fmt.Errorf("invalid placement availability %s", c.SyncConfig.OpenStack.Placement.Availability)
124+
}
113125
return nil
114126
}

internal/conf/validation_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,16 @@ sync:
8484
t.Fatalf("expected error, got nil")
8585
}
8686
}
87+
88+
func TestInvalidConf_InvalidServiceAvailability(t *testing.T) {
89+
content := `
90+
sync:
91+
openstack:
92+
placement:
93+
availability: whatever
94+
`
95+
conf := newConfigFromBytes([]byte(content))
96+
if err := conf.Validate(); err == nil {
97+
t.Fatalf("expected error, got nil")
98+
}
99+
}

internal/conf/yaml.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,16 @@ type SyncOpenStackKeystoneConfig struct {
9090

9191
// Configuration for the nova service.
9292
type SyncOpenStackNovaConfig struct {
93-
// The URL of the nova service.
94-
URL string `yaml:"url"`
93+
// Availability of the service, such as "public", "internal", or "admin".
94+
Availability string `yaml:"availability"`
9595
// The types of resources to sync.
9696
Types []string `yaml:"types"`
9797
}
9898

9999
// Configuration for the placement service.
100100
type SyncOpenStackPlacementConfig struct {
101-
// The URL of the placement service.
102-
URL string `yaml:"url"`
101+
// Availability of the service, such as "public", "internal", or "admin".
102+
Availability string `yaml:"availability"`
103103
// The types of resources to sync.
104104
Types []string `yaml:"types"`
105105
}

internal/sync/openstack/keystone.go

+20-1
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,30 @@ import (
1616
// Type alias for the OpenStack keystone configuration.
1717
type KeystoneConf = conf.SyncOpenStackKeystoneConfig
1818

19+
// KeystoneAPI for OpenStack.
1920
type KeystoneAPI interface {
21+
// Authenticate against the OpenStack keystone.
2022
Authenticate(context.Context) error
23+
// Get the OpenStack provider client.
2124
Client() *gophercloud.ProviderClient
25+
// Find the endpoint for the given service type and availability.
26+
FindEndpoint(availability, serviceType string) (string, error)
2227
}
2328

29+
// KeystoneAPI implementation.
2430
type keystoneAPI struct {
25-
client *gophercloud.ProviderClient
31+
// OpenStack provider client.
32+
client *gophercloud.ProviderClient
33+
// OpenStack keystone configuration.
2634
keystoneConf KeystoneConf
2735
}
2836

37+
// Create a new OpenStack keystone API.
2938
func newKeystoneAPI(keystoneConf KeystoneConf) KeystoneAPI {
3039
return &keystoneAPI{keystoneConf: keystoneConf}
3140
}
3241

42+
// Authenticate against OpenStack keystone.
3343
func (api *keystoneAPI) Authenticate(ctx context.Context) error {
3444
if api.client != nil {
3545
// Already authenticated.
@@ -64,6 +74,15 @@ func (api *keystoneAPI) Authenticate(ctx context.Context) error {
6474
return nil
6575
}
6676

77+
// Find the endpoint for the given service type and availability.
78+
func (api *keystoneAPI) FindEndpoint(availability, serviceType string) (string, error) {
79+
return api.client.EndpointLocator(gophercloud.EndpointOpts{
80+
Type: serviceType,
81+
Availability: gophercloud.Availability(availability),
82+
})
83+
}
84+
85+
// Get the OpenStack provider client.
6786
func (api *keystoneAPI) Client() *gophercloud.ProviderClient {
6887
return api.client
6988
}

internal/sync/openstack/keystone_test.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import (
1212
"github.com/gophercloud/gophercloud/v2"
1313
)
1414

15-
type mockKeystoneAPI struct{}
15+
type mockKeystoneAPI struct {
16+
url string
17+
}
1618

1719
func (m *mockKeystoneAPI) Authenticate(ctx context.Context) error {
1820
return nil
@@ -22,6 +24,10 @@ func (m *mockKeystoneAPI) Client() *gophercloud.ProviderClient {
2224
return &gophercloud.ProviderClient{}
2325
}
2426

27+
func (m *mockKeystoneAPI) FindEndpoint(availability, serviceType string) (string, error) {
28+
return m.url, nil
29+
}
30+
2531
//nolint:gocritic
2632
func setupKeystoneMockServer(handler http.HandlerFunc) (*httptest.Server, KeystoneConf) {
2733
server := httptest.NewServer(handler)

internal/sync/openstack/nova_api.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,18 @@ func (api *novaAPI) Init(ctx context.Context) {
4646
if err := api.keystoneAPI.Authenticate(ctx); err != nil {
4747
panic(err)
4848
}
49+
// Automatically fetch the nova endpoint from the keystone service catalog.
50+
provider := api.keystoneAPI.Client()
51+
serviceType := "compute"
52+
url, err := api.keystoneAPI.FindEndpoint(api.conf.Availability, serviceType)
53+
if err != nil {
54+
panic(err)
55+
}
56+
slog.Info("using nova endpoint", "url", url)
4957
api.sc = &gophercloud.ServiceClient{
50-
ProviderClient: api.keystoneAPI.Client(),
51-
// For some reason gophercloud expects a trailing slash.
52-
Endpoint: api.conf.URL + "/",
53-
Type: "compute",
58+
ProviderClient: provider,
59+
Endpoint: url,
60+
Type: serviceType,
5461
}
5562
}
5663

internal/sync/openstack/nova_api_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ import (
1515

1616
func setupNovaMockServer(handler http.HandlerFunc) (*httptest.Server, KeystoneAPI) {
1717
server := httptest.NewServer(handler)
18-
return server, &mockKeystoneAPI{}
18+
return server, &mockKeystoneAPI{url: server.URL + "/"}
1919
}
2020

2121
func TestNewNovaAPI(t *testing.T) {
2222
mon := sync.Monitor{}
2323
k := &mockKeystoneAPI{}
24-
conf := NovaConf{URL: "http://example.com"}
24+
conf := NovaConf{}
2525

2626
api := newNovaAPI(mon, k, conf)
2727
if api == nil {
@@ -44,7 +44,7 @@ func TestNovaAPI_GetAllServers(t *testing.T) {
4444
defer server.Close()
4545

4646
mon := sync.Monitor{}
47-
conf := NovaConf{URL: server.URL}
47+
conf := NovaConf{Availability: "public"}
4848

4949
api := newNovaAPI(mon, k, conf).(*novaAPI)
5050
api.Init(t.Context())
@@ -76,7 +76,7 @@ func TestNovaAPI_GetAllHypervisors(t *testing.T) {
7676
defer server.Close()
7777

7878
mon := sync.Monitor{}
79-
conf := NovaConf{URL: server.URL}
79+
conf := NovaConf{Availability: "public"}
8080

8181
api := newNovaAPI(mon, k, conf).(*novaAPI)
8282
api.Init(t.Context())

internal/sync/openstack/placement_api.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,18 @@ func (api *placementAPI) Init(ctx context.Context) {
4949
if err := api.keystoneAPI.Authenticate(ctx); err != nil {
5050
panic(err)
5151
}
52+
// Automatically fetch the placement endpoint from the keystone service catalog.
53+
provider := api.keystoneAPI.Client()
54+
serviceType := "placement"
55+
url, err := api.keystoneAPI.FindEndpoint(api.conf.Availability, serviceType)
56+
if err != nil {
57+
panic(err)
58+
}
59+
slog.Info("using placement endpoint", "url", url)
5260
api.sc = &gophercloud.ServiceClient{
53-
ProviderClient: api.keystoneAPI.Client(),
54-
// For some reason gophercloud expects a trailing slash.
55-
Endpoint: api.conf.URL + "/",
56-
Type: "placement",
61+
ProviderClient: provider,
62+
Endpoint: url,
63+
Type: serviceType,
5764
// Needed, otherwise openstack will return 404s for traits.
5865
Microversion: "1.29",
5966
}

internal/sync/openstack/placement_api_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ import (
1414

1515
func setupPlacementMockServer(handler http.HandlerFunc) (*httptest.Server, KeystoneAPI) {
1616
server := httptest.NewServer(handler)
17-
return server, &mockKeystoneAPI{}
17+
return server, &mockKeystoneAPI{url: server.URL + "/"}
1818
}
1919

2020
func TestNewPlacementAPI(t *testing.T) {
2121
mon := sync.Monitor{}
2222
k := &mockKeystoneAPI{}
23-
conf := PlacementConf{URL: "http://example.com"}
23+
conf := PlacementConf{}
2424

2525
api := NewPlacementAPI(mon, k, conf)
2626
if api == nil {
@@ -40,7 +40,7 @@ func TestPlacementAPI_GetAllResourceProviders(t *testing.T) {
4040
defer server.Close()
4141

4242
mon := sync.Monitor{}
43-
conf := PlacementConf{URL: server.URL}
43+
conf := PlacementConf{}
4444

4545
api := NewPlacementAPI(mon, k, conf).(*placementAPI)
4646
api.Init(t.Context())
@@ -70,7 +70,7 @@ func TestPlacementAPI_GetAllTraits(t *testing.T) {
7070
defer server.Close()
7171

7272
mon := sync.Monitor{}
73-
conf := PlacementConf{URL: server.URL}
73+
conf := PlacementConf{}
7474

7575
api := NewPlacementAPI(mon, pc, conf).(*placementAPI)
7676
api.Init(t.Context())
@@ -101,7 +101,7 @@ func TestPlacementAPI_GetAllTraits_Error(t *testing.T) {
101101
defer server.Close()
102102

103103
mon := sync.Monitor{}
104-
conf := PlacementConf{URL: server.URL}
104+
conf := PlacementConf{}
105105

106106
api := NewPlacementAPI(mon, pc, conf).(*placementAPI)
107107
api.Init(t.Context())

0 commit comments

Comments
 (0)