diff --git a/.github/workflows/build-catnip-docker-image.yml b/.github/workflows/build-catnip-docker-image.yml new file mode 100644 index 000000000..287aa4c41 --- /dev/null +++ b/.github/workflows/build-catnip-docker-image.yml @@ -0,0 +1,35 @@ +name: Create and publish a Docker image with the "catnip" app + +on: + push: + branches: ['develop'] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: cloudfoundry/catnip-app + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + attestations: write + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push Docker image + id: push + uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + with: + context: ./assets/catnip/ + file: ./assets/catnip/Dockerfile + push: true + tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest diff --git a/README.md b/README.md index 6e9650f6a..8e624babf 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ include_app_syslog_tcp * `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_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. The CF CLI version must be at least v8.9.0. * `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` @@ -359,7 +359,7 @@ Test Group Name| Description `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. -`file-based service bindings`| Tests file-based service bindings for a buildpack and a CNB app. +`file-based service bindings`| Tests file-based service bindings for a buildpack app, a CNB app and a Docker app. `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. `isolation_segments` | This test group requires that Diego be deployed with a minimum of 2 cells. One of those cells must have been deployed with a `placement_tag`. If the deployment has been deployed with a routing isolation segment, `isolation_segment_domain` must also be set. For more information, please refer to the [Isolation Segments documentation](https://docs.cloudfoundry.org/adminguide/isolation-segments.html). `route_services` | Tests the [Route Services](https://docs.cloudfoundry.org/services/route-services.html) feature of Cloud Foundry. diff --git a/assets/catnip/Dockerfile b/assets/catnip/Dockerfile new file mode 100644 index 000000000..3605fec9a --- /dev/null +++ b/assets/catnip/Dockerfile @@ -0,0 +1,8 @@ +FROM golang +WORKDIR /go/src/app +COPY ./ ./ + +RUN go build -o /go/bin/catnip /go/src/app/main.go + +ENV PORT=8080 +CMD ["/go/bin/catnip"] \ No newline at end of file diff --git a/cats_suite_helpers/cats_suite_helpers.go b/cats_suite_helpers/cats_suite_helpers.go index 0d452d62f..2020eaec3 100644 --- a/cats_suite_helpers/cats_suite_helpers.go +++ b/cats_suite_helpers/cats_suite_helpers.go @@ -112,6 +112,7 @@ func CNBDescribe(description string, callback func()) bool { const ( BuildpackLifecycle string = "buildpack" CNBLifecycle = "CNB" + DockerLifecycle = "Docker" ) func FileBasedServiceBindingsDescribe(description string, lifecycle string, callback func()) bool { @@ -123,6 +124,9 @@ func FileBasedServiceBindingsDescribe(description string, lifecycle string, call if lifecycle == CNBLifecycle && (!Config.GetIncludeFileBasedServiceBindings() || !Config.GetIncludeCNB()) { Skip(skip_messages.SkipFileBasedServiceBindingsCnbApp) } + if lifecycle == DockerLifecycle && (!Config.GetIncludeFileBasedServiceBindings() || !Config.GetIncludeDocker()) { + Skip(skip_messages.SkipFileBasedServiceBindingsDockerApp) + } }) Describe(description, callback) }) diff --git a/file_based_service_bindings/file_based_service_bindings.go b/file_based_service_bindings/file_based_service_bindings.go index 114621cb1..9ab6aba8e 100644 --- a/file_based_service_bindings/file_based_service_bindings.go +++ b/file_based_service_bindings/file_based_service_bindings.go @@ -25,6 +25,10 @@ var _ = FileBasedServiceBindingsDescribe("Enabling file based service binding fo callback(CNBLifecycle) }) +var _ = FileBasedServiceBindingsDescribe("Enabling file based service binding for a Docker app", DockerLifecycle, func() { + callback(DockerLifecycle) +}) + var callback = func(lifecycle string) { var appName, serviceName string @@ -77,6 +81,11 @@ var callback = func(lifecycle string) { if lifecycle == CNBLifecycle { Expect(cf.Cf("create-app", appName, "--app-type", "cnb", "--buildpack", Config.GetGoBuildpackName()).Wait()).To(Exit(0)) } + + if lifecycle == DockerLifecycle { + Expect(cf.Cf("create-app", appName, "--app-type", "docker").Wait()).To(Exit(0)) + } + appGuid := app_helpers.GetAppGuid(appName) appFeatureUrl := fmt.Sprintf("/v3/apps/%s/features/file-based-service-bindings", appGuid) @@ -101,6 +110,15 @@ var callback = func(lifecycle string) { ).Wait(Config.CfPushTimeoutDuration())).To(Exit(0)) } + if lifecycle == DockerLifecycle { + Expect(cf.Cf( + "push", + appName, + "--docker-image", Config.GetCatnipDockerAppImage(), + "-m", DEFAULT_MEMORY_LIMIT, + ).Wait(Config.CfPushTimeoutDuration())).To(Exit(0)) + } + checkFileContent("binding-guid", getServiceBindingGuid(appGuid, serviceGuid)) checkFileContent("instance-guid", serviceGuid) checkFileContent("instance-name", serviceName) diff --git a/helpers/config/config.go b/helpers/config/config.go index 09edc5f66..ea04fed35 100644 --- a/helpers/config/config.go +++ b/helpers/config/config.go @@ -111,6 +111,7 @@ type CatsConfig interface { SleepTimeoutDuration() time.Duration GetPublicDockerAppImage() string + GetCatnipDockerAppImage() string } func NewCatsConfig(path string) (CatsConfig, error) { diff --git a/helpers/config/config_struct.go b/helpers/config/config_struct.go index 4bc05184d..af3be5b1c 100644 --- a/helpers/config/config_struct.go +++ b/helpers/config/config_struct.go @@ -116,6 +116,7 @@ type config struct { PrivateDockerRegistryUsername *string `json:"private_docker_registry_username"` PrivateDockerRegistryPassword *string `json:"private_docker_registry_password"` PublicDockerAppImage *string `json:"public_docker_app_image"` + CatnipDockerAppImage *string `json:"catnip_docker_app_image"` UnallocatedIPForSecurityGroup *string `json:"unallocated_ip_for_security_group"` @@ -240,6 +241,7 @@ func getDefaults() config { defaults.PrivateDockerRegistryUsername = ptrToString("") defaults.PrivateDockerRegistryPassword = ptrToString("") defaults.PublicDockerAppImage = ptrToString("cloudfoundry/diego-docker-app:latest") + defaults.CatnipDockerAppImage = ptrToString("ghcr.io/cloudfoundry/catnip-app:latest") defaults.UnallocatedIPForSecurityGroup = ptrToString("10.0.244.255") @@ -292,6 +294,11 @@ func validateConfig(config *config) error { } + err = validateCatnipDockerAppImage(config) + if err != nil { + errs = errors.Join(errs, err) + } + err = validatePrivateDockerRegistry(config) if err != nil { errs = errors.Join(errs, err) @@ -602,6 +609,16 @@ func validatePublicDockerAppImage(config *config) error { return nil } +func validateCatnipDockerAppImage(config *config) error { + if config.CatnipDockerAppImage == nil { + return fmt.Errorf("* 'catnip_docker_app_image' must not be null") + } + if config.GetCatnipDockerAppImage() == "" { + return fmt.Errorf("* Invalid configuration: 'catnip_docker_app_image' must be set to a valid image source") + } + return nil +} + func validatePrivateDockerRegistry(config *config) error { if config.IncludePrivateDockerRegistry == nil { return fmt.Errorf("* 'include_private_docker_registry' must not be null") @@ -1123,6 +1140,10 @@ func (c *config) GetPublicDockerAppImage() string { return *c.PublicDockerAppImage } +func (c *config) GetCatnipDockerAppImage() string { + return *c.CatnipDockerAppImage +} + func (c *config) GetUnallocatedIPForSecurityGroup() string { return *c.UnallocatedIPForSecurityGroup } diff --git a/helpers/config/config_test.go b/helpers/config/config_test.go index 106a5ec3e..5b6a823f0 100644 --- a/helpers/config/config_test.go +++ b/helpers/config/config_test.go @@ -42,6 +42,7 @@ type testConfig struct { PrivateDockerRegistryUsername *string `json:"private_docker_registry_username,omitempty"` PrivateDockerRegistryPassword *string `json:"private_docker_registry_password,omitempty"` PublicDockerAppImage *string `json:"public_docker_app_image,omitempty"` + CatnipDockerAppImage *string `json:"catnip_docker_app_image,omitempty"` IsolationSegmentName *string `json:"isolation_segment_name,omitempty"` IsolationSegmentDomain *string `json:"isolation_segment_domain,omitempty"` @@ -633,6 +634,32 @@ var _ = Describe("Config", func() { }) }) + Context("when including catnip_docker_app_image", func() { + Context("when image name is set", func() { + var image = "some-image" + BeforeEach(func() { + testCfg.CatnipDockerAppImage = ptrToString(image) + }) + + It("has the value in the config", func() { + config, err := cfg.NewCatsConfig(tmpFilePath) + Expect(err).NotTo(HaveOccurred()) + Expect(config.GetCatnipDockerAppImage()).To(Equal(image)) + }) + }) + + Context("when image is an empty string", func() { + BeforeEach(func() { + testCfg.CatnipDockerAppImage = ptrToString("") + }) + + It("returns an error", func() { + _, err := cfg.NewCatsConfig(tmpFilePath) + Expect(err).To(MatchError("* Invalid configuration: 'catnip_docker_app_image' must be set to a valid image source")) + }) + }) + }) + Context("when including isolation segment tests", func() { BeforeEach(func() { testCfg.IncludeIsolationSegments = ptrToBool(true) diff --git a/helpers/skip_messages/skip_messages.go b/helpers/skip_messages/skip_messages.go index 28b1fad23..d56072649 100644 --- a/helpers/skip_messages/skip_messages.go +++ b/helpers/skip_messages/skip_messages.go @@ -11,6 +11,7 @@ const SkipCNBMessage = `Skipping this test because config.IncludeCNB is set to ' NOTE: Ensure CNB lifecycle is enabled on your platform before enabling this test.` const SkipFileBasedServiceBindingsBuildpackApp = `Skipping this test because config.IncludeFileBasedServiceBindings is set to 'false'.` const SkipFileBasedServiceBindingsCnbApp = `Skipping this test because config.IncludeFileBasedServiceBindings and/or config.IncludeCNB are set to 'false'.` +const SkipFileBasedServiceBindingsDockerApp = `Skipping this test because config.IncludeFileBasedServiceBindings and/or config.IncludeDocker are set to 'false'.` 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'.