Skip to content

Commit 66cca9d

Browse files
authored
Merge pull request #425 from robertjsullivan/support-additional-env-var-types
Support parsing of environment variables other than strings.
2 parents 0ff4362 + 7cf4fa1 commit 66cca9d

File tree

7 files changed

+186
-14
lines changed

7 files changed

+186
-14
lines changed

client/app_test.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ package client
22

33
import (
44
"context"
5+
"github.com/cloudfoundry/go-cfclient/v3/resource"
6+
"github.com/stretchr/testify/require"
57
"net/http"
68
"testing"
79

8-
"github.com/cloudfoundry/go-cfclient/v3/resource"
910
"github.com/cloudfoundry/go-cfclient/v3/testutil"
10-
11-
"github.com/stretchr/testify/require"
1211
)
1312

1413
func TestApps(t *testing.T) {
@@ -20,11 +19,11 @@ func TestApps(t *testing.T) {
2019
space1 := g.Space().JSON
2120
space2 := g.Space().JSON
2221
org := g.Organization().JSON
23-
appEnvironment := g.AppEnvironment().JSON
22+
appEnvironment := g.AppEnvironment()
23+
appEnvironmentExpected := g.AppEnvironmentExpected(appEnvironment.Name).JSON
2424
appEnvVar := g.AppEnvVar().JSON
2525
appSSH := g.AppSSH().JSON
2626
appPermission := g.AppPermission().JSON
27-
2827
tests := []RouteTest{
2928
{
3029
Description: "Create app",
@@ -104,10 +103,10 @@ func TestApps(t *testing.T) {
104103
Route: testutil.MockRoute{
105104
Method: "GET",
106105
Endpoint: "/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/env",
107-
Output: g.Single(appEnvironment),
106+
Output: g.Single(appEnvironment.JSON),
108107
Status: http.StatusOK,
109108
},
110-
Expected: appEnvironment,
109+
Expected: appEnvironmentExpected,
111110
Action: func(c *Client, t *testing.T) (any, error) {
112111
return c.Applications.GetEnvironment(context.Background(), "1cb006ee-fb05-47e1-b541-c34179ddc446")
113112
},
@@ -140,7 +139,7 @@ func TestApps(t *testing.T) {
140139
Output: g.Single(appEnvVar),
141140
Status: http.StatusOK,
142141
},
143-
Expected: `{ "RAILS_ENV": "production" }`,
142+
Expected: `{ "RAILS_ENV": "production", "SOME_BOOLEAN": "true", "SOME_FLOAT64": "10.4", "SOME_INT": "5" }`,
144143
Action: func(c *Client, t *testing.T) (any, error) {
145144
return c.Applications.GetEnvironmentVariables(context.Background(), "1cb006ee-fb05-47e1-b541-c34179ddc446")
146145
},

client/revision_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func TestRevisions(t *testing.T) {
3838
Output: g.Single(appEnvVar),
3939
Status: http.StatusOK,
4040
},
41-
Expected: `{ "RAILS_ENV": "production" }`,
41+
Expected: `{ "RAILS_ENV": "production", "SOME_BOOLEAN":"true", "SOME_FLOAT64":"10.4", "SOME_INT":"5" }`,
4242
Action: func(c *Client, t *testing.T) (any, error) {
4343
return c.Revisions.GetEnvironmentVariables(context.Background(), "5a49a370-92cd-4091-bb62-e0914460f7b2")
4444
},

resource/app.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package resource
22

33
import (
4+
"C"
45
"encoding/json"
6+
"fmt"
7+
"strconv"
58
)
69

710
type App struct {
@@ -132,3 +135,96 @@ func NewAppCreate(name, spaceGUID string) *AppCreate {
132135
},
133136
}
134137
}
138+
139+
type InterfaceEnvVar struct {
140+
Var map[string]interface{} `json:"var"`
141+
}
142+
143+
func (f *EnvVar) UnmarshalJSON(b []byte) error {
144+
s := string(b)
145+
if s == "" || s == "\"\"" {
146+
return nil
147+
}
148+
149+
var interfaceEnvVar InterfaceEnvVar
150+
151+
err := json.Unmarshal(b, &interfaceEnvVar)
152+
153+
if err != nil {
154+
return err
155+
}
156+
157+
varMap := make(map[string]*string)
158+
for key, value := range interfaceEnvVar.Var {
159+
switch v := value.(type) {
160+
default:
161+
fmt.Printf("unexpected env var type, skipping env var: %T", v)
162+
continue
163+
case int:
164+
stringVal := strconv.Itoa(v)
165+
varMap[key] = &stringVal
166+
case string:
167+
varMap[key] = &v
168+
case bool:
169+
stringBool := strconv.FormatBool(v)
170+
varMap[key] = &stringBool
171+
case float64:
172+
stringFloat := strconv.FormatFloat(v, 'f', -1, 64)
173+
varMap[key] = &stringFloat
174+
}
175+
}
176+
f.Var = varMap
177+
178+
return nil
179+
}
180+
181+
type InterfaceAppEnvironment struct {
182+
EnvVars map[string]interface{} `json:"environment_variables,omitempty"`
183+
StagingEnv map[string]interface{} `json:"staging_env_json,omitempty"`
184+
RunningEnv map[string]interface{} `json:"running_env_json,omitempty"`
185+
SystemEnvVars map[string]json.RawMessage `json:"system_env_json,omitempty"` // VCAP_SERVICES
186+
AppEnvVars map[string]json.RawMessage `json:"application_env_json,omitempty"` // VCAP_APPLICATION
187+
}
188+
189+
func (f *AppEnvironment) UnmarshalJSON(b []byte) error {
190+
s := string(b)
191+
if s == "" || s == "\"\"" {
192+
return nil
193+
}
194+
195+
var interfaceAppEnvironment InterfaceAppEnvironment
196+
197+
err := json.Unmarshal(b, &interfaceAppEnvironment)
198+
199+
if err != nil {
200+
return err
201+
}
202+
203+
f.EnvVars = convertEnvVars(interfaceAppEnvironment.EnvVars)
204+
f.StagingEnv = convertEnvVars(interfaceAppEnvironment.StagingEnv)
205+
f.RunningEnv = convertEnvVars(interfaceAppEnvironment.RunningEnv)
206+
f.AppEnvVars = interfaceAppEnvironment.AppEnvVars
207+
f.SystemEnvVars = interfaceAppEnvironment.SystemEnvVars
208+
209+
return nil
210+
}
211+
212+
func convertEnvVars(envVars map[string]interface{}) map[string]string {
213+
envVarMap := make(map[string]string)
214+
for key, value := range envVars {
215+
switch v := value.(type) {
216+
default:
217+
fmt.Printf("unexpected env var type, skipping env var: %T", v)
218+
continue
219+
case int:
220+
envVarMap[key] = strconv.Itoa(v)
221+
case string:
222+
envVarMap[key] = v
223+
case bool:
224+
envVarMap[key] = strconv.FormatBool(v)
225+
case float64:
226+
envVarMap[key] = strconv.FormatFloat(v, 'f', -1, 64)
227+
}
228+
}
229+
return envVarMap
230+
}

testutil/object_generator.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ func (o ObjectJSONGenerator) AppEnvironment() *JSONResource {
119119
return o.renderTemplate(r, "app_environment.json")
120120
}
121121

122+
func (o ObjectJSONGenerator) AppEnvironmentExpected(name string) *JSONResource {
123+
r := &JSONResource{
124+
Name: name,
125+
}
126+
return o.renderTemplate(r, "app_environment_expected.json")
127+
}
128+
122129
func (o ObjectJSONGenerator) AppEnvVar() *JSONResource {
123130
r := &JSONResource{}
124131
return o.renderTemplate(r, "app_envvar.json")

testutil/template/app_environment.json

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
{
22
"staging_env_json": {
3-
"GEM_CACHE": "http://gem-cache.example.org"
3+
"GEM_CACHE": "http://gem-cache.example.org",
4+
"SOME_BOOLEAN": true,
5+
"SOME_INT": 5,
6+
"SOME_FLOAT64": 10.4
47
},
58
"running_env_json": {
6-
"HTTP_PROXY": "http://proxy.example.org"
9+
"HTTP_PROXY": "http://proxy.example.org",
10+
"SOME_BOOLEAN": true,
11+
"SOME_INT": 5,
12+
"SOME_FLOAT64": 10.4
713
},
814
"environment_variables": {
9-
"RAILS_ENV": "production"
15+
"RAILS_ENV": "production",
16+
"SOME_BOOLEAN": true,
17+
"SOME_INT": 5,
18+
"SOME_FLOAT64": 10.4
1019
},
1120
"system_env_json": {
1221
"VCAP_SERVICES": {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"staging_env_json": {
3+
"GEM_CACHE": "http://gem-cache.example.org",
4+
"SOME_BOOLEAN": "true",
5+
"SOME_INT": "5",
6+
"SOME_FLOAT64": "10.4"
7+
},
8+
"running_env_json": {
9+
"HTTP_PROXY": "http://proxy.example.org",
10+
"SOME_BOOLEAN": "true",
11+
"SOME_INT": "5",
12+
"SOME_FLOAT64": "10.4"
13+
},
14+
"environment_variables": {
15+
"RAILS_ENV": "production",
16+
"SOME_BOOLEAN": "true",
17+
"SOME_INT": "5",
18+
"SOME_FLOAT64": "10.4"
19+
},
20+
"system_env_json": {
21+
"VCAP_SERVICES": {
22+
"mysql": [
23+
{
24+
"name": "db-for-my-app",
25+
"binding_id": "0e85b634-e043-4b43-96da-f83dfe83ab33",
26+
"binding_name": "db-for-my-app",
27+
"instance_id": "07fca01c-f789-4d45-80b4-e19ba3ca862c",
28+
"instance_name": "my-mysql-service",
29+
"label": "mysql",
30+
"tags": ["relational", "sql"],
31+
"plan": "xlarge",
32+
"credentials": {
33+
"username": "user",
34+
"password": "top-secret"
35+
},
36+
"syslog_drain_url": "https://syslog.example.org/drain",
37+
"volume_mounts": [],
38+
"provider": null
39+
}
40+
]
41+
}
42+
},
43+
"application_env_json": {
44+
"VCAP_APPLICATION": {
45+
"limits": {
46+
"fds": 16384
47+
},
48+
"application_name": "{{.Name}}",
49+
"application_uris": [ "{{.Name}}.example.org" ],
50+
"name": "{{.Name}}",
51+
"space_name": "my_space",
52+
"space_id": "2f35885d-0c9d-4423-83ad-fd05066f8576",
53+
"uris": [ "my_app.example.org" ],
54+
"users": null
55+
}
56+
}
57+
}

testutil/template/app_envvar.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
{
22
"var": {
3-
"RAILS_ENV": "production"
3+
"RAILS_ENV": "production",
4+
"SOME_BOOLEAN": true,
5+
"SOME_INT": 5,
6+
"SOME_FLOAT64": 10.4
47
},
58
"links": {
69
"self": {
@@ -10,4 +13,5 @@
1013
"href": "https://api.example.org/v3/apps/[guid]"
1114
}
1215
}
13-
}
16+
}
17+

0 commit comments

Comments
 (0)