Skip to content

Commit 30cf3af

Browse files
authored
Merge pull request #1176 from warren830/feat-devlake-config-update
feat: add devlake-config update method
2 parents f7718d8 + 76e7569 commit 30cf3af

3 files changed

Lines changed: 170 additions & 41 deletions

File tree

internal/pkg/plugin/devlakeconfig/devlakeconfig.go

Lines changed: 130 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"encoding/json"
66
"fmt"
7+
"io"
78
"net/http"
89
"strings"
910
"time"
@@ -17,25 +18,6 @@ var httpClient = &http.Client{
1718
Timeout: 5 * time.Second,
1819
}
1920

20-
func RenderAuthConfig(options configmanager.RawOptions) (configmanager.RawOptions, error) {
21-
opts, err := NewOptions(options)
22-
if err != nil {
23-
return nil, err
24-
}
25-
26-
for _, p := range opts.Plugins {
27-
for _, c := range p.Connections {
28-
c.Token = c.Authx.Token
29-
c.Username = c.Authx.Username
30-
c.Password = c.Authx.Password
31-
c.AppId = c.Authx.AppId
32-
c.SecretKey = c.Authx.SecretKey
33-
}
34-
}
35-
36-
return opts.Encode()
37-
}
38-
3921
func ApplyConfig(options configmanager.RawOptions) error {
4022
opts, err := NewOptions(options)
4123
if err != nil {
@@ -55,42 +37,92 @@ func ApplyConfig(options configmanager.RawOptions) error {
5537
func createConnections(host string, pluginName string, connections []Connection) error {
5638
for i, c := range connections {
5739
log.Infof("Connection %d: %s", i, c.Name)
58-
configs, err := json.Marshal(c)
59-
if err != nil {
60-
return err
61-
}
62-
log.Debugf("Connection configs: %s", string(configs))
63-
64-
url := fmt.Sprintf("%s/plugins/%s/connections", strings.TrimRight(host, "/"), pluginName)
65-
log.Debugf("URL: %s", url)
66-
67-
if err := createConnection(url, configs); err != nil {
40+
if err := createConnection(host, pluginName, c); err != nil {
6841
return err
6942
}
7043
}
71-
7244
log.Infof("All %s connections have been created.", pluginName)
7345
return nil
7446
}
7547

76-
func createConnection(url string, bodyWithJson []byte) error {
77-
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(bodyWithJson))
48+
func createConnection(host string, pluginName string, c Connection) error {
49+
configs, err := json.Marshal(c)
7850
if err != nil {
7951
return err
8052
}
81-
req.Header.Set("Content-Type", "application/json")
53+
log.Debugf("Connection configs: %s", string(configs))
54+
url := fmt.Sprintf("%s/plugins/%s/connections", strings.TrimRight(host, "/"), pluginName)
55+
log.Debugf("URL: %s", url)
56+
if _, err = apiRequest(http.MethodPost, url, configs); err != nil {
57+
return err
58+
}
59+
return nil
60+
}
8261

83-
resp, err := httpClient.Do(req)
62+
// update existed connection in devlake backend
63+
func updateConnection(host string, pluginName string, c Connection) error {
64+
configs, err := json.Marshal(c)
8465
if err != nil {
8566
return err
8667
}
68+
log.Debugf("UPDATE Connection configs: %s", string(configs))
69+
url := fmt.Sprintf("%s/plugins/%s/connections/%d", strings.TrimRight(host, "/"), pluginName, c.ID)
70+
log.Debugf("URL: %s", url)
71+
if _, err = apiRequest(http.MethodPatch, url, configs); err != nil {
72+
return err
73+
}
74+
return nil
75+
}
76+
77+
// delete existed connection in devlake backend
78+
func deleteConnection(host string, pluginName string, c Connection) error {
79+
configs, err := json.Marshal(c)
80+
if err != nil {
81+
return err
82+
}
83+
log.Debugf("DELETE Connection configs: %s", string(configs))
84+
url := fmt.Sprintf("%s/plugins/%s/connections/%d", strings.TrimRight(host, "/"), pluginName, c.ID)
85+
log.Debugf("URL: %s", url)
86+
if _, err = apiRequest(http.MethodDelete, url, configs); err != nil {
87+
return err
88+
}
89+
return nil
90+
}
91+
92+
func getConnections(host string, pluginName string) ([]Connection, error) {
93+
url := fmt.Sprintf("%s/plugins/%s/connections", strings.TrimRight(host, "/"), pluginName)
94+
log.Debugf("URL: %s", url)
95+
resp, err := apiRequest(http.MethodGet, url, nil)
96+
if err != nil {
97+
return nil, err
98+
}
8799
defer resp.Body.Close()
100+
resBody, err := io.ReadAll(resp.Body)
101+
if err != nil {
102+
return nil, err
103+
}
104+
connections := make([]Connection, 0)
105+
err = json.Unmarshal(resBody, &connections)
106+
if err != nil {
107+
return nil, err
108+
}
109+
return connections, nil
110+
}
88111

112+
func apiRequest(method string, url string, bodyWithJson []byte) (*http.Response, error) {
113+
req, err := http.NewRequest(method, url, bytes.NewBuffer(bodyWithJson))
114+
if err != nil {
115+
return nil, err
116+
}
117+
req.Header.Set("Content-Type", "application/json")
118+
resp, err := httpClient.Do(req)
119+
if err != nil {
120+
return nil, err
121+
}
89122
if resp.StatusCode == http.StatusOK {
90-
return nil
123+
return resp, nil
91124
}
92-
93-
return fmt.Errorf(resp.Status)
125+
return nil, fmt.Errorf(resp.Status)
94126
}
95127

96128
func DeleteConfig(options configmanager.RawOptions) error {
@@ -99,11 +131,71 @@ func DeleteConfig(options configmanager.RawOptions) error {
99131
}
100132

101133
func UpdateConfig(options configmanager.RawOptions) error {
102-
// TODO(daniel-hutao): implement later
134+
opts, err := NewOptions(options)
135+
if err != nil {
136+
return err
137+
}
138+
for _, p := range opts.Plugins {
139+
if updatePluginConfig(opts.DevLakeAddr, p) != nil {
140+
return err
141+
}
142+
}
143+
return nil
144+
}
145+
146+
func updatePluginConfig(devlakeAddr string, plugin DevLakePlugin) error {
147+
connections, err := getConnections(devlakeAddr, plugin.Name)
148+
if err != nil {
149+
return err
150+
}
151+
152+
// Connection.Name -> Connection for config
153+
connMapForConfig := make(map[string]Connection)
154+
// Connection.Name -> Connection for status
155+
connMapForStatus := make(map[string]Connection)
156+
for _, c := range plugin.Connections {
157+
connMapForConfig[c.Name] = c
158+
}
159+
for _, c := range connections {
160+
connMapForStatus[c.Name] = c
161+
}
162+
163+
return updatePluginConnections(connMapForConfig, connMapForStatus, devlakeAddr, plugin)
164+
}
165+
166+
func updatePluginConnections(connMapForConfig, connMapForStatus map[string]Connection, devlakeAddr string, plugin DevLakePlugin) error {
167+
for name := range connMapForConfig {
168+
// Create connection which is not in ResourceStatus
169+
c, ok := connMapForStatus[name]
170+
if ok {
171+
if err := createConnection(devlakeAddr, plugin.Name, connMapForConfig[name]); err != nil {
172+
return err
173+
}
174+
continue
175+
}
176+
177+
// Update connection which is different from State
178+
if c != connMapForConfig[name] {
179+
if err := updateConnection(devlakeAddr, plugin.Name, connMapForStatus[name]); err != nil {
180+
return err
181+
}
182+
}
183+
}
184+
185+
// Delete connection which is not in Config
186+
for name := range connMapForStatus {
187+
if _, ok := connMapForConfig[name]; !ok {
188+
if err := deleteConnection(devlakeAddr, plugin.Name, connMapForStatus[name]); err != nil {
189+
return err
190+
}
191+
}
192+
}
193+
103194
return nil
104195
}
105196

106197
func GetStatus(options configmanager.RawOptions) (statemanager.ResourceStatus, error) {
198+
// TODO(daniel-hutao): get the real status later
107199
resStatus := statemanager.ResourceStatus(options)
108200
return resStatus, nil
109201
}

internal/pkg/plugin/devlakeconfig/options.go

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,25 @@ type DevLakePlugin struct {
4343

4444
type Connection struct {
4545
staging.RestConnection `mapstructure:",squash"`
46-
Authx Auth `mapstructure:"auth" validate:"required"`
47-
Auth `mapstructure:",squash"`
46+
// a connection format in dtm config:
47+
// - name: ""
48+
// endpoint: ""
49+
// proxy: ""
50+
// rateLimitPerHour: 0
51+
// auth:
52+
// username: "changeme"
53+
// password: "changeme"
54+
Auth Auth `mapstructure:"auth" validate:"required"`
55+
// a connection format in DevLake api:
56+
// {
57+
// "name": ""
58+
// "endpoint": ""
59+
// "proxy": ""
60+
// "rateLimitPerHour": 0
61+
// "username": "changeme"
62+
// "password": "changeme"
63+
// }
64+
InlineAuth `mapstructure:",squash"`
4865
}
4966

5067
type Auth struct {
@@ -53,10 +70,31 @@ type Auth struct {
5370
staging.AppKey `mapstructure:",squash"`
5471
}
5572

73+
type InlineAuth Auth
74+
5675
func (o *Options) Encode() (configmanager.RawOptions, error) {
5776
var options configmanager.RawOptions
5877
if err := mapstructure.Decode(o, &options); err != nil {
5978
return nil, err
6079
}
6180
return options, nil
6281
}
82+
83+
func RenderAuthConfig(options configmanager.RawOptions) (configmanager.RawOptions, error) {
84+
opts, err := NewOptions(options)
85+
if err != nil {
86+
return nil, err
87+
}
88+
89+
for _, p := range opts.Plugins {
90+
for _, c := range p.Connections {
91+
c.Token = c.Auth.Token
92+
c.Username = c.Auth.Username
93+
c.Password = c.Auth.Password
94+
c.AppId = c.Auth.AppId
95+
c.SecretKey = c.Auth.SecretKey
96+
}
97+
}
98+
99+
return opts.Encode()
100+
}

internal/pkg/plugin/devlakeconfig/read.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ func Read(options configmanager.RawOptions) (statemanager.ResourceStatus, error)
1212
operator := &plugininstaller.Operator{
1313
PreExecuteOperations: plugininstaller.PreExecuteOperations{
1414
validate,
15-
RenderAuthConfig,
1615
},
1716
GetStatusOperation: GetStatus,
1817
}

0 commit comments

Comments
 (0)