diff --git a/README.md b/README.md index bbc5dd451..5291953c5 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,7 @@ include_v3 * `include_app_syslog_tcp`: Flag to include the app syslog drain over TCP test group. * `include_apps`: Flag to include the apps test group. * `readiness_health_checks_enabled`: Defaults to `true`. Set to false if you are using an environment without readiness health checks. +* `include_cnb`: Flag to include tests related to building apps using Cloud Native Buildpacks. Diego must be deployed and the CC API diego_cnb feature flag must be enabled for these tests to pass. * `include_container_networking`: Flag to include tests related to container networking. * `credhub_mode`: Valid values are `assisted` or `non-assisted`. [See below](#credhub-modes). * `credhub_location`: Location of CredHub instance; default is `https://credhub.service.cf.internal:8844` @@ -124,7 +125,7 @@ include_v3 * `credhub_secret`: UAA client secret for Service Broker write access to CredHub (required for CredHub tests). * `include_deployments`: Flag to include tests for the cloud controller rolling deployments. V3 must also be enabled. * `include_detect`: Flag to include tests in the detect group. -* `include_docker`: Flag to include tests related to running Docker apps on Diego. Diego must be deployed and the CC API docker_diego feature flag must be enabled for these tests to pass. +* `include_docker`: Flag to include tests related to running Docker apps on Diego. Diego must be deployed and the CC API diego_docker feature flag must be enabled for these tests to pass. * `include_http2_routing`: Flag to include the HTTP/2 Routing tests. * `include_internet_dependent`: Flag to include tests that require the deployment to have internet access. * `include_isolation_segments`: Flag to include isolation segment tests. @@ -175,6 +176,7 @@ include_v3 * `go_buildpack_name` [See below](#buildpack-names) * `r_buildpack_name` [See below](#buildpack-names) * `binary_buildpack_name` [See below](#buildpack-names) +* `cnb_nodejs_buildpack_name` [See below](#buildpack-names) * `include_windows`: Flag to include the tests that run against Windows cells. * `use_windows_test_task`: Flag to include the tasks tests on Windows cells. Default is `false`. @@ -202,6 +204,10 @@ Many tests specify a buildpack when pushing an app, so that on diego the app sta * `binary_buildpack_name: binary_buildpack` * `hwc_buildpack_name: hwc_buildpack` +For the Cloud Native Buildpacks lifecycle, you can override them by setting different names: + +* `cnb_nodejs_buildpack_name: docker://gcr.io/paketo-buildpacks/nodejs:latest` + #### Route Services Test Group Setup The `route_services` test group pushes applications which must be able to reach the load balancer of your Cloud Foundry deployment. This requires configuring application security groups to support this. Your deployment manifest should include the following data if you are running the `route_services` group: @@ -350,6 +356,7 @@ Test Group Name| Description `app_syslog_tcp`| Tests the ability to configure an app syslog drain listener. `apps`| Tests the core functionalities of Cloud Foundry: staging, running, logging, routing, buildpacks, etc. This test group should always pass against a sound Cloud Foundry deployment. `credhub`| Tests CredHub-delivered Secure Service credentials in the service binding. [CredHub configuration][credhub-secure-service-credentials] is required to run these tests. In addition to selecting a `credhub_mode`, `credhub_client` and `credhub_secret` values are required for these tests. +`cnb` | Tests our ability to use cloud native buildpacks. `detect` | Tests the ability of the platform to detect the correct buildpack for compiling an application if no buildpack is explicitly specified. `docker`| Tests our ability to run docker containers on Diego and that we handle docker metadata correctly. `internet_dependent`| Tests the feature of being able to specify a buildpack via a Github URL. As such, this depends on your Cloud Foundry application containers having access to the Internet. You should take into account the configuration of the network into which you've deployed your Cloud Foundry, as well as any security group settings applied to application containers. diff --git a/assets/cnb-node/manifest.yml b/assets/cnb-node/manifest.yml new file mode 100644 index 000000000..716dc3b33 --- /dev/null +++ b/assets/cnb-node/manifest.yml @@ -0,0 +1,3 @@ +--- +applications: + - lifecycle: cnb diff --git a/assets/cnb-node/package.json b/assets/cnb-node/package.json new file mode 100644 index 000000000..1f703ab9c --- /dev/null +++ b/assets/cnb-node/package.json @@ -0,0 +1,4 @@ +{ + "name" : "node_without_procfile", + "version": "0.1.0" +} diff --git a/assets/cnb-node/server.js b/assets/cnb-node/server.js new file mode 100644 index 000000000..21ef82533 --- /dev/null +++ b/assets/cnb-node/server.js @@ -0,0 +1,16 @@ +var http = require('http'); +var url = require('url'); + +HOST = null; + +var host = "0.0.0.0"; +var port = process.env.PORT || 3000; + +http.createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/html'}); + res.write('

Hello from a node app! '); + res.write('via: ' + host + ':' + port); + res.end('

'); +}).listen(port, null); + +console.log('Server running at http://' + host + ':' + port + '/'); diff --git a/cats_suite_helpers/cats_suite_helpers.go b/cats_suite_helpers/cats_suite_helpers.go index dd8727d8c..908f9e4cd 100644 --- a/cats_suite_helpers/cats_suite_helpers.go +++ b/cats_suite_helpers/cats_suite_helpers.go @@ -97,6 +97,17 @@ func DockerDescribe(description string, callback func()) bool { }) } +func CNBDescribe(description string, callback func()) bool { + return Describe("[cnb]", func() { + BeforeEach(func() { + if !Config.GetIncludeCNB() { + Skip(skip_messages.SkipCNBMessage) + } + }) + Describe(description, callback) + }) +} + func InternetDependentDescribe(description string, callback func()) bool { return Describe("[internet_dependent]", func() { BeforeEach(func() { diff --git a/cats_suite_test.go b/cats_suite_test.go index 8f46779e4..438f53803 100644 --- a/cats_suite_test.go +++ b/cats_suite_test.go @@ -14,6 +14,7 @@ import ( _ "github.com/cloudfoundry/cf-acceptance-tests/app_syslog_tcp" _ "github.com/cloudfoundry/cf-acceptance-tests/apps" + _ "github.com/cloudfoundry/cf-acceptance-tests/cnb" _ "github.com/cloudfoundry/cf-acceptance-tests/credhub" _ "github.com/cloudfoundry/cf-acceptance-tests/detect" _ "github.com/cloudfoundry/cf-acceptance-tests/docker" diff --git a/cnb/cnb_lifecycle.go b/cnb/cnb_lifecycle.go new file mode 100644 index 000000000..327b4ae21 --- /dev/null +++ b/cnb/cnb_lifecycle.go @@ -0,0 +1,64 @@ +package cnb + +import ( + "path/filepath" + + . "github.com/cloudfoundry/cf-acceptance-tests/cats_suite_helpers" + "github.com/cloudfoundry/cf-acceptance-tests/helpers/app_helpers" + "github.com/cloudfoundry/cf-acceptance-tests/helpers/assets" + "github.com/cloudfoundry/cf-acceptance-tests/helpers/random_name" + "github.com/cloudfoundry/cf-test-helpers/v2/cf" + "github.com/cloudfoundry/cf-test-helpers/v2/helpers" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gbytes" + . "github.com/onsi/gomega/gexec" +) + +var _ = CNBDescribe("CloudNativeBuildpacks lifecycle", func() { + var appName string + + BeforeEach(func() { + appName = random_name.CATSRandomName("CNB-APP") + }) + + AfterEach(func() { + app_helpers.AppReport(appName) + Eventually(cf.Cf("delete", appName, "-f")).Should(Exit(0)) + }) + + Describe("pushing Node.js application with Cloud Native Buildpacks", func() { + It("makes the app reachable via its bound route", func() { + Expect(cf.Cf("push", + appName, + "-p", assets.NewAssets().CNBNode, + "-b", Config.GetCNBNodejsBuildpackName(), + "-f", filepath.Join(assets.NewAssets().CNBNode, "manifest.yml"), + ).Wait(Config.CfPushTimeoutDuration())).To(Exit(0)) + + Eventually(func() string { + return helpers.CurlAppRoot(Config, appName) + }).Should(ContainSubstring("Hello from a node app!")) + }) + }) + + Describe("pushing Node.js application with CNB lifecycle and no buildpacks", func() { + It("fails", func() { + push := cf.Cf("push", + appName, + "-p", assets.NewAssets().CNBNode, + "-f", filepath.Join(assets.NewAssets().CNBNode, "manifest.yml"), + ).Wait(Config.CfPushTimeoutDuration()) + + Expect(push).To(Exit(1)) + Expect(combineOutput(push.Out, push.Err)).To(Say("Buildpack\\(s\\) must be specified when using Cloud Native Buildpacks")) + }) + }) +}) + +func combineOutput(outBuffer *Buffer, errBuffer *Buffer) *Buffer { + combinedOutput := BufferWithBytes(outBuffer.Contents()) + combinedOutput.Write(errBuffer.Contents()) + return combinedOutput +} diff --git a/helpers/assets/assets.go b/helpers/assets/assets.go index 28ea92357..7afd0e865 100644 --- a/helpers/assets/assets.go +++ b/helpers/assets/assets.go @@ -22,6 +22,7 @@ type Assets struct { Python string Nginx string Node string + CNBNode string NodeWithProcfile string Nora string Pora string @@ -67,6 +68,7 @@ func NewAssets() Assets { LoggregatorLoadGeneratorGo: "assets/loggregator-load-generator-go", Nginx: "assets/nginx", Node: "assets/node", + CNBNode: "assets/cnb-node", NodeWithProcfile: "assets/node-with-procfile", Nora: "assets/nora/NoraPublished", Pora: "assets/pora", diff --git a/helpers/config/config.go b/helpers/config/config.go index 191a6e4ef..e32d9ced5 100644 --- a/helpers/config/config.go +++ b/helpers/config/config.go @@ -12,6 +12,7 @@ type CatsConfig interface { GetIncludeCredhubNonAssisted() bool GetIncludeDetect() bool GetIncludeDocker() bool + GetIncludeCNB() bool GetIncludeInternetDependent() bool GetIncludePrivateDockerRegistry() bool GetIncludeRouteServices() bool @@ -70,6 +71,7 @@ type CatsConfig interface { GetNamePrefix() string GetNginxBuildpackName() string GetNodejsBuildpackName() string + GetCNBNodejsBuildpackName() string GetPrivateDockerRegistryImage() string GetPrivateDockerRegistryUsername() string GetPrivateDockerRegistryPassword() string diff --git a/helpers/config/config_struct.go b/helpers/config/config_struct.go index edb338332..78017d9dd 100644 --- a/helpers/config/config_struct.go +++ b/helpers/config/config_struct.go @@ -61,6 +61,8 @@ type config struct { RubyBuildpackName *string `json:"ruby_buildpack_name"` StaticFileBuildpackName *string `json:"staticfile_buildpack_name"` + CNBNodejsBuildpackName *string `json:"cnb_nodejs_buildpack_name"` + VolumeServiceName *string `json:"volume_service_name"` VolumeServicePlanName *string `json:"volume_service_plan_name"` VolumeServiceCreateConfig *string `json:"volume_service_create_config"` @@ -71,6 +73,7 @@ type config struct { IncludeDeployments *bool `json:"include_deployments"` IncludeDetect *bool `json:"include_detect"` IncludeDocker *bool `json:"include_docker"` + IncludeCNB *bool `json:"include_cnb"` IncludeInternetDependent *bool `json:"include_internet_dependent"` IncludeIsolationSegments *bool `json:"include_isolation_segments"` IncludePrivateDockerRegistry *bool `json:"include_private_docker_registry"` @@ -157,6 +160,8 @@ func getDefaults() config { defaults.RubyBuildpackName = ptrToString("ruby_buildpack") defaults.StaticFileBuildpackName = ptrToString("staticfile_buildpack") + defaults.CNBNodejsBuildpackName = ptrToString("docker://gcr.io/paketo-buildpacks/nodejs:latest") + defaults.IncludeAppSyslogTCP = ptrToBool(true) defaults.IncludeApps = ptrToBool(true) defaults.IncludeDetect = ptrToBool(true) @@ -170,6 +175,7 @@ func getDefaults() config { defaults.CredhubClientName = ptrToString("credhub_admin_client") defaults.CredhubClientSecret = ptrToString("") defaults.IncludeDocker = ptrToBool(false) + defaults.IncludeCNB = ptrToBool(false) defaults.IncludeInternetDependent = ptrToBool(false) defaults.IncludeIsolationSegments = ptrToBool(false) defaults.IncludeTCPIsolationSegments = ptrToBool(false) @@ -400,6 +406,9 @@ func validateConfig(config *config) error { if config.StaticFileBuildpackName == nil { errs = errors.Join(errs, fmt.Errorf("* 'staticfile_buildpack_name' must not be null")) } + if config.CNBNodejsBuildpackName == nil { + errs = errors.Join(errs, fmt.Errorf("* 'cnb_nodejs_buildpack_name' must not be null")) + } if config.IncludeAppSyslogTCP == nil { errs = errors.Join(errs, fmt.Errorf("* 'include_app_syslog_tcp' must not be null")) } @@ -415,6 +424,9 @@ func validateConfig(config *config) error { if config.IncludeDocker == nil { errs = errors.Join(errs, fmt.Errorf("* 'include_docker' must not be null")) } + if config.IncludeCNB == nil { + errs = errors.Join(errs, fmt.Errorf("* 'include_cnb' must not be null")) + } if config.IncludeInternetDependent == nil { errs = errors.Join(errs, fmt.Errorf("* 'include_internet_dependent' must not be null")) } @@ -901,6 +913,10 @@ func (c *config) GetIncludeDocker() bool { return *c.IncludeDocker } +func (c *config) GetIncludeCNB() bool { + return *c.IncludeCNB +} + func (c *config) GetIncludeInternetDependent() bool { return *c.IncludeInternetDependent } @@ -1049,6 +1065,10 @@ func (c *config) GetStaticFileBuildpackName() string { return *c.StaticFileBuildpackName } +func (c *config) GetCNBNodejsBuildpackName() string { + return *c.CNBNodejsBuildpackName +} + func (c *config) GetPrivateDockerRegistryImage() string { return *c.PrivateDockerRegistryImage } diff --git a/helpers/skip_messages/skip_messages.go b/helpers/skip_messages/skip_messages.go index 04c456a3f..85dc2d383 100644 --- a/helpers/skip_messages/skip_messages.go +++ b/helpers/skip_messages/skip_messages.go @@ -7,6 +7,8 @@ const SkipContainerNetworkingMessage = `Skipping this test because config.Includ const SkipDetectMessage = `Skipping this test because config.IncludeDetect is set to 'false'.` const SkipDockerMessage = `Skipping this test because config.IncludeDocker is set to 'false'. NOTE: Ensure Docker containers are enabled on your platform before enabling this test.` +const SkipCNBMessage = `Skipping this test because config.IncludeCNB is set to 'false'. +NOTE: Ensure CNB lifecycle is enabled on your platform before enabling this test.` const SkipInternetDependentMessage = `Skipping this test because config.IncludeInternetDependent is set to 'false'. NOTE: Ensure that your platform has access to the internet before running this test.` const SkipPrivateDockerRegistryMessage = `Skipping this test because config.IncludePrivateDockerRegistry is set to 'false'.