Skip to content

Commit 6ebc3cc

Browse files
authored
Features in manifest (#1575)
* Test file-based service bindings features in manifest Add tests that enable features and bind services via manifest; keep tests that use the API instead. Test 'file-based-vcap-services' and 'service-binding-k8s' features for buildpack, cnb and docker apps. Refactor test setup; the three lifecycles need different args for 'create-app' and 'push', otherwise operations are identical. Also enhance test validation; read VCAP_SERVICES_FILE_PATH and SERVICE_BINDING_ROOT env vars from app. * Test ssh feature in manifest Add tests that disable ssh via cli command and manifest. Also as part of Windows tests.
1 parent c3060b4 commit 6ebc3cc

File tree

5 files changed

+242
-51
lines changed

5 files changed

+242
-51
lines changed

file_based_service_bindings/file_based_service_bindings.go

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,41 @@ var _ = FileBasedServiceBindingsDescribe("Enabling file based service binding fo
2626

2727
var callback = func(lifeCycle LifeCycle) {
2828
var appName, serviceName string
29+
2930
BeforeEach(func() {
3031
appName = random_name.CATSRandomName("APP")
3132
serviceName = generator.PrefixedRandomName("cats", "svin") // uppercase characters are not valid
3233
})
3334

34-
Context("When the file-based-vcap-services feature enabled", func() {
35-
It("It should store the VCAP_SERVICE binding information in file in the VCAP_SERVICES_FILE_PATH", func() {
36-
appGuid, serviceGuid := lifeCycle.Prepare(serviceName, appName, "file-based-vcap-services")
37-
services.ValidateFileBasedVcapServices(appName, serviceName, appGuid, serviceGuid)
35+
Context("When the file-based-vcap-services feature is enabled", func() {
36+
Context("Via API call", func() {
37+
It("It should store the VCAP_SERVICES binding information in a file in the VCAP_SERVICES_FILE_PATH", func() {
38+
appGuid, serviceGuid := Prepare(appName, serviceName, "file-based-vcap-services", lifeCycle)
39+
services.ValidateFileBasedVcapServices(appName, serviceName, appGuid, serviceGuid)
40+
})
41+
})
3842

43+
Context("Via manifest", func() {
44+
It("It should store the VCAP_SERVICES binding information in a file in the VCAP_SERVICES_FILE_PATH", func() {
45+
appGuid, serviceGuid := PrepareWithManifest(appName, serviceName, "file-based-vcap-services", lifeCycle)
46+
services.ValidateFileBasedVcapServices(appName, serviceName, appGuid, serviceGuid)
47+
})
3948
})
4049
})
4150

42-
Context("When the service-binding-k8s feature enabled", func() {
43-
It("It should have environment variable SERVICE_BINDING_ROOT which defines the location for the service binding", func() {
44-
appGuid, serviceGuid := lifeCycle.Prepare(serviceName, appName, "service-binding-k8s")
45-
services.ValidateServiceBindingK8s(appName, serviceName, appGuid, serviceGuid)
51+
Context("When the service-binding-k8s feature is enabled", func() {
52+
Context("Via API call", func() {
53+
It("It should store the binding information in files under the SERVICE_BINDING_ROOT path", func() {
54+
appGuid, serviceGuid := Prepare(appName, serviceName, "service-binding-k8s", lifeCycle)
55+
services.ValidateServiceBindingK8s(appName, serviceName, appGuid, serviceGuid)
56+
})
57+
})
58+
59+
Context("Via manifest", func() {
60+
It("It should store the binding information in files under the SERVICE_BINDING_ROOT path", func() {
61+
appGuid, serviceGuid := PrepareWithManifest(appName, serviceName, "service-binding-k8s", lifeCycle)
62+
services.ValidateServiceBindingK8s(appName, serviceName, appGuid, serviceGuid)
63+
})
4664
})
4765
})
4866

@@ -52,5 +70,4 @@ var callback = func(lifeCycle LifeCycle) {
5270
Eventually(cf.Cf("delete", appName, "-f")).Should(Exit(0))
5371
Eventually(cf.Cf("delete-service", serviceName, "-f").Wait()).Should(Exit(0))
5472
})
55-
5673
}

file_based_service_bindings/life_cycles.go

Lines changed: 97 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,77 +10,136 @@ import (
1010
"github.com/cloudfoundry/cf-test-helpers/v2/cf"
1111
. "github.com/onsi/gomega"
1212
. "github.com/onsi/gomega/gexec"
13+
"os"
14+
"path/filepath"
1315
)
1416

1517
type LifeCycle interface {
16-
Prepare(string, string, string) (string, string)
18+
CreateAppArgs() []string
19+
PushArgs() []string
1720
}
1821

1922
type BuildpackLifecycles struct{}
2023
type CNBLifecycles struct{}
2124
type DockerLifecycles struct{}
2225

23-
func (b *BuildpackLifecycles) Prepare(serviceName, appName, appFeatureFlag string) (string, string) {
24-
25-
Expect(cf.Cf("create-app", appName).Wait()).To(Exit(0))
26-
appGuid, serviceGuid := LifeCycleCommon(serviceName, appName, appFeatureFlag)
27-
28-
Expect(cf.Cf(app_helpers.CatnipWithArgs(
29-
appName,
30-
"-m", DEFAULT_MEMORY_LIMIT)...,
31-
).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
32-
33-
return appGuid, serviceGuid
34-
26+
func (b *BuildpackLifecycles) CreateAppArgs() []string {
27+
return []string{}
3528
}
3629

37-
func (c *CNBLifecycles) Prepare(serviceName, appName, appFeatureFlag string) (string, string) {
30+
func (b *BuildpackLifecycles) PushArgs() []string {
31+
return []string{
32+
"--buildpack", Config.GetBinaryBuildpackName(),
33+
"-p", assets.NewAssets().Catnip,
34+
"-c", "./catnip",
35+
}
36+
}
3837

39-
Expect(cf.Cf("create-app", appName, "--app-type", "cnb", "--buildpack", Config.GetGoBuildpackName()).Wait()).To(Exit(0))
40-
appGuid, serviceGuid := LifeCycleCommon(serviceName, appName, appFeatureFlag)
38+
func (c *CNBLifecycles) CreateAppArgs() []string {
39+
return []string{
40+
"--app-type", "cnb",
41+
"--buildpack", Config.GetCNBGoBuildpackName(),
42+
}
43+
}
4144

42-
Expect(cf.Cf(
43-
"push",
44-
appName,
45+
func (c *CNBLifecycles) PushArgs() []string {
46+
return []string{
4547
"--lifecycle", "cnb",
4648
"--buildpack", Config.GetCNBGoBuildpackName(),
47-
"-m", DEFAULT_MEMORY_LIMIT,
4849
"-p", assets.NewAssets().CatnipSrc,
49-
).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
50-
51-
return appGuid, serviceGuid
50+
}
5251
}
5352

54-
func (d *DockerLifecycles) Prepare(serviceName, appName, appFeatureFlag string) (string, string) {
55-
56-
Expect(cf.Cf("create-app", appName, "--app-type", "docker").Wait()).To(Exit(0))
57-
appGuid, serviceGuid := LifeCycleCommon(serviceName, appName, appFeatureFlag)
53+
func (d *DockerLifecycles) CreateAppArgs() []string {
54+
return []string{
55+
"--app-type", "docker",
56+
}
57+
}
5858

59-
Expect(cf.Cf(
60-
"push",
61-
appName,
59+
func (d *DockerLifecycles) PushArgs() []string {
60+
return []string{
6261
"--docker-image", Config.GetCatnipDockerAppImage(),
63-
"-m", DEFAULT_MEMORY_LIMIT,
64-
).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
65-
return appGuid, serviceGuid
62+
}
6663
}
6764

6865
const (
6966
TAGS = "list, of, tags"
7067
CREDS = `{"username": "admin", "password":"pa55woRD"}`
7168
)
7269

73-
func LifeCycleCommon(serviceName, appName, appFeatureFlag string) (string, string) {
70+
func Prepare(appName, serviceName, appFeatureFlag string, lifecycle LifeCycle) (string, string) {
71+
appGuid := CreateApp(appName, lifecycle.CreateAppArgs()...)
72+
serviceGuid := CreateUpsi(serviceName)
73+
BindUpsi(appName, serviceName)
74+
EnableFeatureViaAPI(appGuid, appFeatureFlag)
75+
76+
pushArgs := append([]string{appName}, lifecycle.PushArgs()...)
77+
Push(pushArgs...)
78+
79+
return appGuid, serviceGuid
80+
}
81+
82+
func PrepareWithManifest(appName, serviceName, appFeatureFlag string, lifecycle LifeCycle) (string, string) {
83+
serviceGuid := CreateUpsi(serviceName)
84+
manifestFile := CreateManifest(appName, serviceName, appFeatureFlag)
85+
86+
pushArgs := append([]string{"-f", manifestFile}, lifecycle.PushArgs()...)
87+
Push(pushArgs...)
88+
89+
return app_helpers.GetAppGuid(appName), serviceGuid
90+
}
91+
92+
func CreateApp(appName string, args ...string) string {
93+
createAppArgs := []string{
94+
"create-app", appName,
95+
}
96+
createAppArgs = append(createAppArgs, args...)
97+
Expect(cf.Cf(createAppArgs...).Wait()).To(Exit(0))
98+
appGuid := app_helpers.GetAppGuid(appName)
7499

100+
return appGuid
101+
}
102+
103+
func CreateUpsi(serviceName string) string {
75104
Expect(cf.Cf("create-user-provided-service", serviceName, "-p", CREDS, "-t", TAGS).Wait()).To(Exit(0))
76105
serviceGuid := services.GetServiceInstanceGuid(serviceName)
77106

78-
appGuid := app_helpers.GetAppGuid(appName)
107+
return serviceGuid
108+
}
79109

80-
appFeatureUrl := fmt.Sprintf("/v3/apps/%s/features/%s", appGuid, appFeatureFlag)
81-
Expect(cf.Cf("curl", appFeatureUrl, "-X", "PATCH", "-d", `{"enabled": true}`).Wait()).To(Exit(0))
110+
func CreateManifest(appName, serviceName, appFeatureFlag string) string {
111+
tmpdir, err := os.MkdirTemp(os.TempDir(), appName)
112+
Expect(err).ToNot(HaveOccurred())
113+
114+
manifestFile := filepath.Join(tmpdir, "manifest.yml")
115+
manifestContent := fmt.Sprintf(`---
116+
applications:
117+
- name: %s
118+
features:
119+
%s: true
120+
services:
121+
- %s
122+
`, appName, appFeatureFlag, serviceName)
123+
err = os.WriteFile(manifestFile, []byte(manifestContent), 0644)
124+
Expect(err).ToNot(HaveOccurred())
125+
126+
return manifestFile
127+
}
82128

129+
func BindUpsi(appName, serviceName string) {
83130
Expect(cf.Cf("bind-service", appName, serviceName).Wait()).To(Exit(0))
131+
}
84132

85-
return appGuid, serviceGuid
133+
func EnableFeatureViaAPI(appGuid, appFeatureFlag string) {
134+
appFeatureUrl := fmt.Sprintf("/v3/apps/%s/features/%s", appGuid, appFeatureFlag)
135+
Expect(cf.Cf("curl", appFeatureUrl, "-X", "PATCH", "-d", `{"enabled": true}`).Wait()).To(Exit(0))
136+
}
137+
138+
func Push(args ...string) {
139+
pushArgs := []string{
140+
"push",
141+
"-m", DEFAULT_MEMORY_LIMIT,
142+
}
143+
pushArgs = append(pushArgs, args...)
144+
Expect(cf.Cf(pushArgs...).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
86145
}

helpers/services/service_helper.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,16 @@ func GetServiceBindingGuid(appGuid string, instanceGuid string) string {
4747
}
4848

4949
func ValidateServiceBindingK8s(appName, serviceName, appGuid, serviceGuid string) {
50+
serviceBindingRoot := helpers.CurlApp(Config, appName, "/env/SERVICE_BINDING_ROOT")
51+
Expect(serviceBindingRoot).Should(Equal("/etc/cf-service-bindings"))
52+
5053
getEncodedFilepath := func(serviceName string, fileName string) string {
51-
path := fmt.Sprintf("/etc/cf-service-bindings/%s/%s", serviceName, fileName)
54+
path := fmt.Sprintf("%s/%s/%s", serviceBindingRoot, serviceName, fileName)
5255
return strings.Replace(path, "/", "%2F", -1)
5356
}
5457

5558
checkFileContent := func(fileName string, content string) {
56-
curlResponse := helpers.CurlApp(Config, appName, "/file/"+getEncodedFilepath(serviceName, fileName), "-L")
59+
curlResponse := helpers.CurlApp(Config, appName, "/file/"+getEncodedFilepath(serviceName, fileName))
5760
Expect(curlResponse).Should(ContainSubstring(content))
5861
}
5962

@@ -70,8 +73,11 @@ func ValidateServiceBindingK8s(appName, serviceName, appGuid, serviceGuid string
7073
}
7174

7275
func ValidateFileBasedVcapServices(appName, serviceName, appGuid, serviceGuid string) {
76+
vcapServicesFilePath := helpers.CurlApp(Config, appName, "/env/VCAP_SERVICES_FILE_PATH")
77+
Expect(vcapServicesFilePath).Should(Equal("/etc/cf-service-bindings/vcap_services"))
78+
7379
getEncodedFilepath := func() string {
74-
return strings.Replace("/etc/cf-service-bindings/vcap_services", "/", "%2F", -1)
80+
return strings.Replace(vcapServicesFilePath, "/", "%2F", -1)
7581
}
7682

7783
expectedVcapServicesTemplate := `{
@@ -101,7 +107,7 @@ func ValidateFileBasedVcapServices(appName, serviceName, appGuid, serviceGuid st
101107
Fail(err.Error())
102108
}
103109

104-
curlResponse := helpers.CurlApp(Config, appName, "/file/"+getEncodedFilepath(), "-L")
110+
curlResponse := helpers.CurlApp(Config, appName, "/file/"+getEncodedFilepath())
105111
actualJson := VCAPServicesFile{}
106112
err = actualJson.ReadFromString(curlResponse)
107113
if err != nil {

ssh/ssh.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@ import (
44
"encoding/json"
55
"fmt"
66
"io"
7+
"os"
78
"os/exec"
9+
"path/filepath"
810
"strings"
911

1012
. "github.com/cloudfoundry/cf-acceptance-tests/cats_suite_helpers"
1113

1214
"golang.org/x/crypto/ssh"
1315

1416
"github.com/cloudfoundry/cf-acceptance-tests/helpers/app_helpers"
17+
"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"
1518
"github.com/cloudfoundry/cf-acceptance-tests/helpers/logs"
1619
"github.com/cloudfoundry/cf-acceptance-tests/helpers/random_name"
1720
"github.com/cloudfoundry/cf-test-helpers/v2/cf"
@@ -206,6 +209,30 @@ var _ = SshDescribe("SSH", func() {
206209
return string(cf.Cf("events", appName).Wait().Out.Contents())
207210
}).Should(MatchRegexp("audit.app.ssh-unauthorized"))
208211
})
212+
213+
Context("disabling ssh", func() {
214+
BeforeEach(func() {
215+
Expect(cf.Cf("ssh", "-v", appName).Wait()).To(Exit(0))
216+
})
217+
218+
It("can be disabled via cli", func() {
219+
Expect(cf.Cf("disable-ssh", appName).Wait()).To(Exit(0))
220+
221+
expectSshCmdToFail(appName)
222+
})
223+
224+
It("can be disabled via manifest", func() {
225+
manifestFile := createManifestSshDisabled(appName)
226+
pushArgs := []string{
227+
"push",
228+
"-f", manifestFile,
229+
"-p", assets.NewAssets().Catnip,
230+
}
231+
Expect(cf.Cf(pushArgs...).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
232+
233+
expectSshCmdToFail(appName)
234+
})
235+
})
209236
})
210237

211238
})
@@ -234,3 +261,31 @@ func sshProxyAddress() string {
234261

235262
return response.Links.AppSsh.Href
236263
}
264+
265+
func createManifestSshDisabled(appName string) string {
266+
tmpdir, err := os.MkdirTemp(os.TempDir(), appName)
267+
Expect(err).ToNot(HaveOccurred())
268+
269+
manifestFile := filepath.Join(tmpdir, "manifest.yml")
270+
manifestContent := fmt.Sprintf(`---
271+
applications:
272+
- name: %s
273+
features:
274+
ssh: false
275+
`, appName)
276+
err = os.WriteFile(manifestFile, []byte(manifestContent), 0644)
277+
Expect(err).ToNot(HaveOccurred())
278+
279+
return manifestFile
280+
}
281+
282+
func expectSshCmdToFail(appName string) {
283+
sshCmd := cf.Cf("ssh", "-v", appName)
284+
Expect(sshCmd.Wait()).To(Exit(1))
285+
286+
output := string(sshCmd.Out.Contents())
287+
stdErr := string(sshCmd.Err.Contents())
288+
289+
Expect(string(output)).To(MatchRegexp("FAILED"))
290+
Expect(string(stdErr)).To(MatchRegexp("Error opening SSH connection"))
291+
}

0 commit comments

Comments
 (0)