Skip to content

Commit d6ce55f

Browse files
ryanjohnsontvabe garcia
andauthored
Add db indexes to Gorm config (#179)
* Add db indexes to Gorm config * Update README * Update sql tests --------- Co-authored-by: abe garcia <[email protected]>
1 parent d08af2c commit d6ce55f

File tree

5 files changed

+59
-43
lines changed

5 files changed

+59
-43
lines changed

README.md

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,44 +5,50 @@
55
Go Clouddriver is a rewrite of Spinnaker's [Clouddriver](https://github.com/spinnaker/clouddriver) microservice. It has an observed 95%+ decrease in CPU and memory load for Kubernetes operations.
66

77
Go Clouddriver brings many features to the table which allow it to perform better than Clouddriver OSS for production loads:
8+
89
- it does not rely on `kubectl` and instead interfaces directly with the Kubernetes API for all operations
910
- it utilizes an in-memory cache store for Kubernetes API discovery
1011
- it stores Kubernetes providers in a database, fronted by a simple CRUD API
1112
- it removes over-complicated strategies such as [Cache All The Stuff](https://github.com/spinnaker/clouddriver/tree/master/cats), instead making live calls for all operations
1213

13-
Go Clouddriver is *not* an API complete implementation of Clouddriver OSS and only handles Kubernetes providers and operations. It is meant to be run in tandem with Clouddriver OSS. Visit [the wiki](https://github.com/homedepot/go-clouddriver/wiki) for feature support and intallation instructions.
14+
Go Clouddriver is _not_ an API complete implementation of Clouddriver OSS and only handles Kubernetes providers and operations. It is meant to be run in tandem with Clouddriver OSS. Visit [the wiki](https://github.com/homedepot/go-clouddriver/wiki) for feature support and intallation instructions.
1415

1516
## Getting Started
1617

1718
### Testing
1819

1920
Run from the root directory
21+
2022
```bash
2123
make tools test
2224
```
2325

2426
### Building
2527

2628
Run from the root directory
29+
2730
```bash
2831
make build
2932
```
3033

3134
### Running Locally
3235

33-
1) Go Clouddriver generates its access tokens using [Arcade](https://github.com/homedepot/arcade) as a sidecar, so a working instance of Arcade will need to be running locally in order for Go Clouddriver to talk to Kubernetes clusters.
36+
1. Go Clouddriver generates its access tokens using [Arcade](https://github.com/homedepot/arcade) as a sidecar, so a working instance of Arcade will need to be running locally in order for Go Clouddriver to talk to Kubernetes clusters.
37+
38+
2. Export the Arcade API key (the same one you set up in step 1).
3439

35-
2) Export the Arcade API key (the same one you set up in step 1).
3640
```bash
3741
export ARCADE_API_KEY=test
3842
```
3943

40-
3) Run Go Clouddriver.
44+
3. Run Go Clouddriver.
45+
4146
```bash
4247
make run
4348
```
4449

45-
4) Create your first Kubernetes provider! Go Clouddriver runs on port 7002, so you'll make a POST to `localhost:7002/v1/kubernetes/providers`.
50+
4. Create your first Kubernetes provider! Go Clouddriver runs on port 7002, so you'll make a POST to `localhost:7002/v1/kubernetes/providers`.
51+
4652
```bash
4753
curl -XPOST localhost:7002/v1/kubernetes/providers -d '{
4854
"name": "test-provider",
@@ -58,62 +64,70 @@ curl -XPOST localhost:7002/v1/kubernetes/providers -d '{
5864
}
5965
}' | jq
6066
```
67+
6168
And you should see the response
69+
6270
```json
6371
{
6472
"name": "test-provider",
6573
"host": "https://test-host",
6674
"caData": "test",
6775
"permissions": {
68-
"read": [
69-
"test-group"
70-
],
71-
"write": [
72-
"test-group"
73-
]
76+
"read": ["test-group"],
77+
"write": ["test-group"]
7478
}
7579
}
7680
```
81+
7782
Running the command again will return a `409 Conflict` unless you change the name of the provider.
7883

79-
5) List your providers by calling the `/credentials` endpoint.
84+
5. List your providers by calling the `/credentials` endpoint.
85+
8086
```bash
8187
curl localhost:7002/credentials | jq
8288
```
8389

8490
### Configuration
8591

86-
| Environment Variable | Description | Notes | Default Value |
87-
|----------|:-------------:|-----------:|------------:|
88-
| `ARCADE_API_KEY` | Needed to talk to [Arcade](https://github.com/billiford/arcade). | Required for most operations. ||
89-
| `ARTIFACTS_CREDENTIALS_CONFIG_DIR` | Sets the directory for artifacts configuration. | Optional. Leave unset to use OSS Clouddriver's Artifacts API. ||
90-
| `KUBERNETES_USE_DISK_CACHE` | Stores Kubernetes API discovery on disk instead of in-memory. || `false` |
91-
| `DB_HOST` | Used to connect to MySQL database. | If not set will default to local SQLite database. ||
92-
| `DB_NAME` | Used to connect to MySQL database. | If not set will default to local SQLite database. ||
93-
| `DB_PASS` | Used to connect to MySQL database. | If not set will default to local SQLite database. ||
94-
| `DB_USER` | Used to connect to MySQL database. | If not set will default to local SQLite database. ||
95-
| `VERBOSE_REQUEST_LOGGING` | Logs all incoming request information. | Should only be used in non-production for testing. | `false` |
92+
| Environment Variable | Description | Notes | Default Value |
93+
| ---------------------------------- | :--------------------------------------------------------------: | ------------------------------------------------------------: | ------------: |
94+
| `ARCADE_API_KEY` | Needed to talk to [Arcade](https://github.com/billiford/arcade). | Required for most operations. | |
95+
| `ARTIFACTS_CREDENTIALS_CONFIG_DIR` | Sets the directory for artifacts configuration. | Optional. Leave unset to use OSS Clouddriver's Artifacts API. | |
96+
| `KUBERNETES_USE_DISK_CACHE` | Stores Kubernetes API discovery on disk instead of in-memory. | | `false` |
97+
| `DB_HOST` | Used to connect to MySQL database. | If not set will default to local SQLite database. | |
98+
| `DB_NAME` | Used to connect to MySQL database. | If not set will default to local SQLite database. | |
99+
| `DB_PASS` | Used to connect to MySQL database. | If not set will default to local SQLite database. | |
100+
| `DB_USER` | Used to connect to MySQL database. | If not set will default to local SQLite database. | |
101+
| `VERBOSE_REQUEST_LOGGING` | Logs all incoming request information. | Should only be used in non-production for testing. | `false` |
96102

97103
### MySQL Indexes and Cleanup
98104

99105
Go Clouddriver stores all deployed resource requests in its `kubernetes_resources` table, which needs to be cleaned up periodically. It also requires a few indexes
100-
to work efficiently and properly for continued deployments over long periods of time.
106+
to work efficiently and properly for continued deployments over long periods of time. These are defined by default when
107+
starting the application.
101108

102109
#### Indexes
103110

104111
First, an index to help the Applications API remain efficient.
112+
105113
```sql
106-
CREATE INDEX kind_account_name_kind_name_spinnaker_app_idx ON kubernetes_resources(account_name, kind, name, spinnaker_app);
114+
CREATE INDEX account_name_kind_name_spinnaker_app_idx ON kubernetes_resources(account_name, kind, name, spinnaker_app);
107115
```
116+
108117
Next, an index to assist in pulling "cluster" kinds from the `kubernetes_resources` table.
118+
109119
```sql
110120
CREATE INDEX kind_idx ON kubernetes_resources(kind);
111121
```
122+
112123
Next, an index to assist the Task API.
124+
113125
```sql
114126
CREATE INDEX task_id_idx ON kubernetes_resources(task_id);
115127
```
128+
116129
Finally, a couple of indexes on the provider read/write permissions tables to help the Credentials API and any queries to select providers from the database.
130+
117131
```sql
118132
CREATE INDEX account_name_idx ON provider_read_permissions(account_name);
119133
CREATE INDEX account_name_idx ON provider_write_permissions(account_name);
@@ -123,7 +137,8 @@ CREATE INDEX account_name_idx ON provider_write_permissions(account_name);
123137

124138
The `kubernetes_resources` tables is incredibly important for storing the most-recent state of a cluster for the Kubernetes kinds we care about.
125139

126-
It is recommended to run the following cleanup statements *daily* to maintain a healthy state of onboarded Kubernetes clusters.
140+
It is recommended to run the following cleanup statements _daily_ to maintain a healthy state of onboarded Kubernetes clusters.
141+
127142
```sql
128143
DELETE FROM kubernetes_resources where cluster = '' and timestamp < (NOW() - INTERVAL 6 HOUR);
129144

@@ -141,5 +156,3 @@ WHERE
141156
t1.cluster = t2.cluster AND
142157
t1.timestamp < (NOW() - INTERVAL 6 HOUR);
143158
```
144-
145-

internal/kubernetes/provider.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ func (Provider) TableName() string {
4747

4848
type ProviderNamespaces struct {
4949
//ID string `json:"-" gorm:"primary_key"`
50-
AccountName string `json:"accountName"`
51-
Namespace string `json:"namespace,omitempty"`
50+
AccountName string `json:"accountName" gorm:"index:account_name_namespace_idx,unique"`
51+
Namespace string `json:"namespace,omitempty" gorm:"index:account_name_namespace_idx,unique"`
5252
}
5353

5454
func (ProviderNamespaces) TableName() string {

internal/kubernetes/resource.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@ package kubernetes
33
import "time"
44

55
type Resource struct {
6-
AccountName string `json:"accountName"`
6+
AccountName string `json:"accountName" gorm:"index:account_name_kind_name_spinnaker_app_idx,priority:1"`
77
ID string `json:"id" gorm:"primary_key"`
88
Timestamp time.Time `json:"timestamp,omitempty" gorm:"type:timestamp;DEFAULT:current_timestamp"`
9-
TaskID string `json:"taskId"`
9+
TaskID string `json:"taskId" gorm:"index:task_id_idx"`
1010
TaskType string `json:"-"`
1111
APIGroup string `json:"apiGroup"`
12-
Name string `json:"name"`
12+
Name string `json:"name" gorm:"index:account_name_kind_name_spinnaker_app_idx,priority:3"`
1313
ArtifactName string `json:"-"`
1414
Namespace string `json:"namespace"`
1515
Resource string `json:"resource"`
1616
Version string `json:"version"`
17-
Kind string `json:"kind"`
18-
SpinnakerApp string `json:"spinnakerApp"`
17+
Kind string `json:"kind" gorm:"index:account_name_kind_name_spinnaker_app_idx,priority:2;index:kind_idx"`
18+
SpinnakerApp string `json:"spinnakerApp" gorm:"index:account_name_kind_name_spinnaker_app_idx,priority:4"`
1919
Cluster string `json:"-"`
2020
}
2121

internal/sql/client_test.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ var _ = Describe("Sql", func() {
5252
"\\)$").
5353
WillReturnResult(sqlmock.NewResult(1, 1))
5454
mock.ExpectExec("(?i)^CREATE TABLE `kubernetes_resources` " +
55-
"\\(`account_name`\\ varchar\\(256\\)," +
55+
"\\(`account_name` varchar\\(256\\)," +
5656
"`id` varchar\\(256\\)," +
5757
"`timestamp` timestamp DEFAULT current_timestamp," +
5858
"`task_id` varchar\\(256\\)," +
@@ -66,25 +66,28 @@ var _ = Describe("Sql", func() {
6666
"`kind` varchar\\(256\\)," +
6767
"`spinnaker_app` varchar\\(256\\)," +
6868
"`cluster` varchar\\(256\\)," +
69-
"PRIMARY KEY \\(`id`\\)" +
70-
"\\)$").
71-
WillReturnResult(sqlmock.NewResult(1, 1))
69+
"PRIMARY KEY \\(`id`\\)," +
70+
"INDEX `.*").WillReturnResult(sqlmock.NewResult(1, 1))
7271
mock.ExpectExec("CREATE TABLE `kubernetes_providers_namespaces` " +
7372
"\\(`account_name` varchar\\(256\\)," +
74-
"`namespace` varchar\\(256\\)").
73+
"`namespace` varchar\\(256\\)," +
74+
"UNIQUE INDEX `account_name_namespace_idx` \\(`account_name`,`namespace`\\)" +
75+
"\\)$").
7576
WillReturnResult(sqlmock.NewResult(1, 1))
7677
mock.ExpectExec("(?i)^CREATE TABLE `provider_read_permissions` " +
7778
"\\(`id`\\ varchar\\(256\\)," +
7879
"`account_name` varchar\\(256\\)," +
7980
"`read_group` varchar\\(256\\)," +
80-
"PRIMARY KEY \\(`id`\\)" +
81+
"PRIMARY KEY \\(`id`\\)," +
82+
"INDEX `account_name_idx` \\(`account_name`\\)" +
8183
"\\)$").
8284
WillReturnResult(sqlmock.NewResult(1, 1))
8385
mock.ExpectExec("(?i)^CREATE TABLE `provider_write_permissions` " +
8486
"\\(`id`\\ varchar\\(256\\)," +
8587
"`account_name` varchar\\(256\\)," +
8688
"`write_group` varchar\\(256\\)," +
87-
"PRIMARY KEY \\(`id`\\)" +
89+
"PRIMARY KEY \\(`id`\\)," +
90+
"INDEX `account_name_idx` \\(`account_name`\\)" +
8891
"\\)$").
8992
WillReturnResult(sqlmock.NewResult(1, 1))
9093

pkg/permissions.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ type Permissions struct {
77

88
type ReadPermission struct {
99
ID string `json:"-" gorm:"primary_key"`
10-
AccountName string `json:"accountName"`
10+
AccountName string `json:"accountName" gorm:"index:account_name_idx"`
1111
ReadGroup string `json:"readGroup"`
1212
}
1313

@@ -17,7 +17,7 @@ func (ReadPermission) TableName() string {
1717

1818
type WritePermission struct {
1919
ID string `json:"-" gorm:"primary_key"`
20-
AccountName string `json:"accountName"`
20+
AccountName string `json:"accountName" gorm:"index:account_name_idx"`
2121
WriteGroup string `json:"writeGroup"`
2222
}
2323

0 commit comments

Comments
 (0)