Skip to content

Commit d89e867

Browse files
author
Joshua Anderson
committed
feat(client-go): add config endpoint
1 parent 629b8bc commit d89e867

11 files changed

Lines changed: 755 additions & 24 deletions

File tree

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ include includes.mk
77
# the filepath to this repository, relative to $GOPATH/src
88
repo_path = github.com/deis/deis
99

10-
GO_PACKAGES = pkg/time version
10+
GO_PACKAGES = version
1111
GO_PACKAGES_REPO_PATH = $(addprefix $(repo_path)/,$(GO_PACKAGES))
1212

1313
COMPONENTS=builder cache controller database logger logspout publisher registry router store swarm
@@ -92,7 +92,7 @@ test-functional:
9292

9393
test-unit:
9494
@$(foreach C, $(COMPONENTS), $(MAKE) -C $(C) test-unit &&) echo done
95-
@$(foreach C, $(CLIENTS), $(MAKE) -C $(C) test-unit &&) echo done
95+
@$(foreach C, pkg $(CLIENTS), $(MAKE) -C $(C) test-unit &&) echo done
9696

9797
test-integration:
9898
$(MAKE) -C tests/ test-full
@@ -108,7 +108,7 @@ test-style:
108108
@for i in $(addsuffix /...,$(GO_PACKAGES)); do \
109109
$(GOLINT) $$i; \
110110
done
111-
@$(foreach C, tests $(CLIENTS) $(COMPONENTS), $(MAKE) -C $(C) test-style &&) echo done
111+
@$(foreach C, tests pkg $(CLIENTS) $(COMPONENTS), $(MAKE) -C $(C) test-style &&) echo done
112112

113113
commit-hook:
114114
cp contrib/util/commit-msg .git/hooks/commit-msg

client-go/cmd/apps.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/deis/deis/pkg/prettyprint"
1010

11+
"github.com/deis/deis/client-go/controller/api"
1112
"github.com/deis/deis/client-go/controller/client"
1213
"github.com/deis/deis/client-go/controller/models/apps"
1314
"github.com/deis/deis/client-go/controller/models/config"
@@ -31,10 +32,12 @@ func AppCreate(id string, buildpack string, remote string, noRemote bool) error
3132
fmt.Printf("done, created %s\n", app.ID)
3233

3334
if buildpack != "" {
34-
configValues := map[string]string{
35-
"BUILDPACK_URL": buildpack,
35+
configValues := api.Config{
36+
Values: map[string]interface{}{
37+
"BUILDPACK_URL": buildpack,
38+
},
3639
}
37-
if err = config.Set(c, app.ID, configValues); err != nil {
40+
if _, err = config.Set(c, app.ID, configValues); err != nil {
3841
return err
3942
}
4043
}

client-go/cmd/config.go

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
package cmd
2+
3+
import (
4+
"encoding/base64"
5+
"fmt"
6+
"io/ioutil"
7+
"os"
8+
"regexp"
9+
"strings"
10+
11+
"github.com/deis/deis/pkg/prettyprint"
12+
13+
"github.com/deis/deis/client-go/controller/api"
14+
"github.com/deis/deis/client-go/controller/models/config"
15+
)
16+
17+
// ConfigList lists an app's config.
18+
func ConfigList(appID string, oneLine bool) error {
19+
c, appID, err := load(appID)
20+
21+
if err != nil {
22+
return err
23+
}
24+
25+
config, err := config.List(c, appID)
26+
27+
if err != nil {
28+
return err
29+
}
30+
31+
if oneLine {
32+
for key, value := range config.Values {
33+
fmt.Printf("%s=%s ", key, value)
34+
}
35+
fmt.Println()
36+
} else {
37+
fmt.Printf("=== %s Config\n", appID)
38+
39+
configMap := make(map[string]string)
40+
41+
// config.Values is type interface, so it needs to be converted to a string
42+
for key, value := range config.Values {
43+
configMap[key] = value.(string)
44+
}
45+
46+
fmt.Print(prettyprint.PrettyTabs(configMap, 5))
47+
}
48+
49+
return nil
50+
}
51+
52+
// ConfigSet sets an app's config variables.
53+
func ConfigSet(appID string, configVars []string) error {
54+
c, appID, err := load(appID)
55+
56+
if err != nil {
57+
return err
58+
}
59+
60+
configMap := parseConfig(configVars)
61+
62+
value, ok := configMap["SSH_KEY"]
63+
64+
if ok {
65+
sshKey := value.(string)
66+
67+
if _, err := os.Stat(value.(string)); err == nil {
68+
contents, err := ioutil.ReadFile(value.(string))
69+
70+
if err != nil {
71+
return err
72+
}
73+
74+
sshKey = string(contents)
75+
}
76+
77+
sshRegex := regexp.MustCompile("^-.+ .SA PRIVATE KEY-*")
78+
79+
if !sshRegex.MatchString(sshKey) {
80+
return fmt.Errorf("Could not parse SSH private key:\n %s", sshKey)
81+
}
82+
83+
configMap["SSH_KEY"] = base64.StdEncoding.EncodeToString([]byte(sshKey))
84+
}
85+
86+
fmt.Print("Creating config... ")
87+
88+
quit := progress()
89+
configObj := api.Config{Values: configMap}
90+
_, err = config.Set(c, appID, configObj)
91+
92+
quit <- true
93+
<-quit
94+
95+
if err != nil {
96+
return err
97+
}
98+
99+
fmt.Print("done\n\n")
100+
101+
return ConfigList(appID, false)
102+
}
103+
104+
// ConfigUnset removes a config variable from an app.
105+
func ConfigUnset(appID string, configVars []string) error {
106+
c, appID, err := load(appID)
107+
108+
if err != nil {
109+
return err
110+
}
111+
112+
fmt.Print("Removing config... ")
113+
114+
quit := progress()
115+
116+
configObj := api.Config{}
117+
118+
valuesMap := make(map[string]interface{})
119+
120+
for _, configVar := range configVars {
121+
valuesMap[configVar] = nil
122+
}
123+
124+
configObj.Values = valuesMap
125+
126+
_, err = config.Set(c, appID, configObj)
127+
128+
quit <- true
129+
<-quit
130+
131+
if err != nil {
132+
return err
133+
}
134+
135+
fmt.Print("done\n\n")
136+
137+
return ConfigList(appID, false)
138+
}
139+
140+
// ConfigPull pulls an app's config to a file.
141+
func ConfigPull(appID string, interactive bool, overwrite bool) error {
142+
filename := ".env"
143+
144+
if !overwrite {
145+
if _, err := os.Stat(filename); err == nil {
146+
return fmt.Errorf("%s already exists, pass -o to overwrite", filename)
147+
}
148+
}
149+
150+
c, appID, err := load(appID)
151+
152+
if err != nil {
153+
return err
154+
}
155+
156+
configVars, err := config.List(c, appID)
157+
158+
if interactive {
159+
contents, err := ioutil.ReadFile(filename)
160+
161+
if err != nil {
162+
return err
163+
}
164+
localConfigVars := strings.Split(string(contents), "\n")
165+
166+
configMap := parseConfig(localConfigVars[:len(localConfigVars)-1])
167+
168+
for key, value := range configVars.Values {
169+
localValue, ok := configMap[key]
170+
171+
if ok {
172+
if value != localValue {
173+
var confirm string
174+
fmt.Printf("%s: overwrite %s with %s? (y/N) ", key, localValue, value)
175+
176+
fmt.Scanln(&confirm)
177+
178+
if strings.ToLower(confirm) == "y" {
179+
configMap[key] = value
180+
}
181+
}
182+
} else {
183+
configMap[key] = value
184+
}
185+
}
186+
187+
return ioutil.WriteFile(filename, []byte(formatConfig(configMap)), 0755)
188+
}
189+
190+
return ioutil.WriteFile(filename, []byte(formatConfig(configVars.Values)), 0755)
191+
}
192+
193+
// ConfigPush pushes an app's config from a file.
194+
func ConfigPush(appID string, fileName string) error {
195+
contents, err := ioutil.ReadFile(fileName)
196+
197+
if err != nil {
198+
return err
199+
}
200+
201+
config := strings.Split(string(contents), "\n")
202+
return ConfigSet(appID, config[:len(config)-1])
203+
}
204+
205+
func parseConfig(configVars []string) map[string]interface{} {
206+
configMap := make(map[string]interface{})
207+
208+
regex := regexp.MustCompile("^([A-z_]+[A-z0-9_]*)=(.+)$")
209+
for _, config := range configVars {
210+
if regex.MatchString(config) {
211+
captures := regex.FindStringSubmatch(config)
212+
configMap[captures[1]] = captures[2]
213+
} else {
214+
fmt.Printf("'%s' does not match the pattern 'key=var', ex: MODE=test\n", config)
215+
}
216+
}
217+
218+
return configMap
219+
}
220+
221+
func formatConfig(configVars map[string]interface{}) string {
222+
var formattedConfig string
223+
224+
for key, value := range configVars {
225+
formattedConfig += fmt.Sprintf("%s=%s\n", key, value)
226+
}
227+
228+
return formattedConfig
229+
}

client-go/controller/api/config.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,21 @@ package api
44
type ConfigSet struct {
55
Values map[string]string `json:"values"`
66
}
7+
8+
// ConfigUnset is the definition of POST /v1/apps/<app id>/config/.
9+
type ConfigUnset struct {
10+
Values map[string]interface{} `json:"values"`
11+
}
12+
13+
// Config is the structure of an app's config.
14+
type Config struct {
15+
Owner string `json:"owner,omitempty"`
16+
App string `json:"app,omitempty"`
17+
Values map[string]interface{} `json:"values,omitempty"`
18+
Memory map[string]interface{} `json:"memory,omitempty"`
19+
CPU map[string]interface{} `json:"cpu,omitempty"`
20+
Tags map[string]interface{} `json:"tags,omitempty"`
21+
Created string `json:"created,omitempty"`
22+
Updated string `json:"updated,omitempty"`
23+
UUID string `json:"uuid,omitempty"`
24+
}

client-go/controller/models/config/config.go

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,52 @@ import (
99
"github.com/deis/deis/client-go/controller/client"
1010
)
1111

12-
// Set a config variable.
13-
func Set(c *client.Client, app string, configValues map[string]string) error {
14-
config := api.ConfigSet{Values: configValues}
12+
// List lists an app's config.
13+
func List(c *client.Client, app string) (api.Config, error) {
14+
u := fmt.Sprintf("/v1/apps/%s/config/", app)
1515

16+
body, status, err := c.BasicRequest("GET", u, nil)
17+
18+
if err != nil {
19+
return api.Config{}, err
20+
}
21+
22+
if status != 200 {
23+
return api.Config{}, errors.New(body)
24+
}
25+
26+
config := api.Config{}
27+
if err = json.Unmarshal([]byte(body), &config); err != nil {
28+
return api.Config{}, err
29+
}
30+
31+
return config, nil
32+
}
33+
34+
// Set sets an app's config variables.
35+
func Set(c *client.Client, app string, config api.Config) (api.Config, error) {
1636
body, err := json.Marshal(config)
1737

1838
if err != nil {
19-
return err
39+
return api.Config{}, err
2040
}
2141

22-
url := fmt.Sprintf("/v1/apps/%s/config", app)
42+
u := fmt.Sprintf("/v1/apps/%s/config/", app)
2343

24-
resBody, status, err := c.BasicRequest("POST", url, body)
44+
resBody, status, err := c.BasicRequest("POST", u, body)
2545

2646
if err != nil {
27-
return err
47+
return api.Config{}, err
2848
}
2949

3050
if status != 201 {
31-
return errors.New(resBody)
51+
return api.Config{}, errors.New(resBody)
52+
}
53+
54+
newConfig := api.Config{}
55+
if err = json.Unmarshal([]byte(resBody), &newConfig); err != nil {
56+
return api.Config{}, err
3257
}
3358

34-
return nil
59+
return newConfig, nil
3560
}

0 commit comments

Comments
 (0)