diff --git a/.github/workflows/build-using-cnb-buildpack.yml b/.github/workflows/build-using-cnb-buildpack.yml new file mode 100644 index 0000000..d6d617d --- /dev/null +++ b/.github/workflows/build-using-cnb-buildpack.yml @@ -0,0 +1,81 @@ +name: "Build using cnb buildpack" +permissions: + packages: write +on: + push: + branches: + - '*' + tags: + - "v?[0-9]+.[0-9]+.[0-9]+*" +env: + CNB_IMAGE_NAME: cnb-app + SERVICE_NAME: 'mysql' + SERVICE_IMAGE: "mariadb:11.2-jammy" + SERVICE_HEALTH_CMD: "/usr/local/bin/healthcheck.sh --connect --innodb_initialized" + SERVICE_PORT: 3306 + SERVICE_USERNAME: '22032e25-4aba-417f-a394-8bbd78d920cd' + SERVICE_PASSWORD: 'StbQ4EovUpwQjD0cT1Hr7PKBG' + SERVICE_HOST: "dynamically_generated" + DATABASE_NAME: my-mysql-db-sample + DEBUG: 0 + +jobs: + package-app: + name: "Package ${{ github.repository }} as CNB app" + runs-on: ubuntu-latest + + steps: + - + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + with: + # Number of commits to fetch. 0 indicates all history for all branches and tags. + # Default: 1 + fetch-depth: 1 + - + name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - + name: Setup pack for ${{ github.repository }} + uses: buildpacks/github-actions/setup-pack@v5.0.0 + - + name: Packaging app from ${{ github.repository }} pushed by ${{ github.actor }} + shell: bash + run: | + echo "Pack Build ${{env.CNB_IMAGE_NAME}} (${{github.repository}})" + pack build ${{env.CNB_IMAGE_NAME}} --path assets/sinatra_app + echo "Pack Inspect ${{env.CNB_IMAGE_NAME}} (${{github.repository}})" + pack inspect ${{env.CNB_IMAGE_NAME}} + - + name: Testing app + shell: bash + run: | + source ./setup-prerequisite.sh + ./run-tests.sh + - + name: Tag version ${{github.ref_name}} + if: github.ref_type == 'tag' + run: | + docker tag ${{env.CNB_IMAGE_NAME}}:latest ghcr.io/${{ github.repository }}:${{github.ref_name}} + - + name: Tag with common data + run: | + docker tag ${{env.CNB_IMAGE_NAME}}:latest ghcr.io/${{ github.repository }}:latest + docker tag ${{env.CNB_IMAGE_NAME}}:latest ghcr.io/${{ github.repository }}:${{github.sha}} + echo "cleanup built image" + docker image rm ${{env.CNB_IMAGE_NAME}}:latest + echo "dump existing images" + docker images + - + name: Publish ghcr.io/${{ github.repository }} + run: | + echo "publish all tags" + docker push --all-tags ghcr.io/${{ github.repository }} + + diff --git a/.gitignore b/.gitignore index 87ea540..a96e987 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,10 @@ integration_config.json integration-config.json *.test **/out/ + +/vendor +.idea/ +*.log +*.iml +/vcap-service.env +/assets/sinatra_app/vendor/ diff --git a/assets/sinatra_app/Gemfile b/assets/sinatra_app/Gemfile index f286c1d..6db19ab 100644 --- a/assets/sinatra_app/Gemfile +++ b/assets/sinatra_app/Gemfile @@ -6,4 +6,6 @@ gem 'sinatra' # gem 'thin' gem 'mysql2' gem 'rackup' -gem 'bigdecimal' #warning: bigdecimal was loaded from the standard library, but will no longer be part of the default gems since Ruby 3.4.0. Add bigdecimal to your Gemfile or gemspec. Also contact author of mysql2-0.5.6 to add bigdecimal into its gemspec. +gem 'bigdecimal' # warning: bigdecimal was loaded from the standard library, but will no longer be part of the default gems since Ruby 3.4.0. Add bigdecimal to your Gemfile or gemspec. Also contact author of mysql2-0.5.6 to add bigdecimal into its gemspec. +gem 'logger', '~> 1.6', '>= 1.6.1' # warning: logger was loaded from the standard library, but will no longer be part of the default gems starting from Ruby 3.5.0 +gem 'ostruct', '~> 0.6.0' # warning: ostruct was loaded from the standard library, but will no longer be part of the default gems starting from Ruby 3.5.0 \ No newline at end of file diff --git a/assets/sinatra_app/Gemfile.lock b/assets/sinatra_app/Gemfile.lock index 9454074..bf2754b 100644 --- a/assets/sinatra_app/Gemfile.lock +++ b/assets/sinatra_app/Gemfile.lock @@ -3,9 +3,11 @@ GEM specs: base64 (0.2.0) bigdecimal (3.1.8) + logger (1.6.1) mustermann (3.0.3) ruby2_keywords (~> 0.0.1) mysql2 (0.5.6) + ostruct (0.6.0) rack (3.1.7) rack-protection (4.0.0) base64 (>= 0.1.0) @@ -30,7 +32,9 @@ PLATFORMS DEPENDENCIES bigdecimal + logger (~> 1.6, >= 1.6.1) mysql2 + ostruct (~> 0.6.0) rackup sinatra diff --git a/assets/sinatra_app/project.toml b/assets/sinatra_app/project.toml new file mode 100644 index 0000000..b0d2117 --- /dev/null +++ b/assets/sinatra_app/project.toml @@ -0,0 +1,11 @@ +[_] +schema-version = "0.2" +id = "com.orange-cloudfoundry.sample-apps.mysql" +name = "MySQL sample app" +#version = "1.0.0" + +[io.buildpacks] +builder = "paketobuildpacks/builder-jammy-full" +#include = [ +# "assets/sinatra_app" +#] diff --git a/local.env b/local.env new file mode 100644 index 0000000..26cce2c --- /dev/null +++ b/local.env @@ -0,0 +1,11 @@ +echo "Sourcing local env properties" +export CNB_IMAGE_NAME=mysql-cnb-app +export SERVICE_NAME=mysql +export SERVICE_HEALTH_CMD="/usr/local/bin/healthcheck.sh --connect --innodb_initialized" +export SERVICE_PORT=3306 +export DATABASE_NAME=my-mysql-db-sample +export SERVICE_IMAGE="mariadb:11.2-jammy" +export SERVICE_HOST="dynamically_generated" +export SERVICE_USERNAME='22032e25-4aba-417f-a394-8bbd78d920cd-my-user' +export SERVICE_PASSWORD='StbQ4EovUpwQjD0cT1Hr7PKBG' +#export DEBUG=0 diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/run-tests.sh b/run-tests.sh new file mode 100755 index 0000000..ea971c0 --- /dev/null +++ b/run-tests.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +set -e + +./vcap-services-template-reformat.sh +cat vcap-service.env +source vcap-service.env +if [ -z "$VCAP_SERVICES" ];then + echo "ERROR: empty VCAP_SERVICES env var detected. Please check ./vcap-services-template-reformat.sh" + exit 1 +fi +echo "Starting sample app" +container_name="cnb-app-container" +container_id=$(docker run -d --rm -e PORT=80 -p 8080:80 --env-file vcap-service.env --name "$container_name" ${CNB_IMAGE_NAME}) + +echo "Cnb app started (id: $container_id)" +echo "Waiting to ensure app is up and running" +while [ "$( docker container inspect -f '{{.State.Status}}' $container_name )" != "running" ]; do + echo "waiting for $container_name to be running current: $(docker inspect -f '{{.State.Status}}' $container_name)" + sleep 1 +done +sleep 3 # to ensure app is up and running +service_container_name="$(docker ps -f "ancestor=$SERVICE_IMAGE" --format "{{.Names}}")" +CONTAINER_APP_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $container_name) +echo "CONTAINER_APP_IP: $CONTAINER_APP_IP" + +if [ "$DEBUG" = "1" ]; then + echo "----------------- $container_name --------------------" + docker inspect -f '{{json .NetworkSettings}}' $container_name + + echo "----------------- $SERVICE_NAME: $service_container_name --------------------" + docker inspect -f '{{json .NetworkSettings}}' "$service_container_name" +fi +echo "=== Redirect logs to cnb-app-container.log ===" +docker logs -f cnb-app-container &> cnb-app-container.log & + +echo "=== List running container ===" +docker ps -a + +echo "=== Check connectivity ===" +if nc -vz 127.0.0.1 8080;then echo "port 8080 available";else echo "port 8080 UNAVAILABLE";exit_status=1;fi +if nc -vz 127.0.0.1 "${SERVICE_PORT}";then echo "port ${SERVICE_PORT} available";else echo "port ${SERVICE_PORT} UNAVAILABLE";exit_status=1;fi + +function check_service() { + type="$1" + cmd="$2" + cmd_prefix="$3" + if [ -z "$cmd_prefix" ];then + cmd_prefix="curl" + fi + status=0 + echo "$type using > $cmd <" 1>&2 + if ! $cmd;then + echo "" 1>&2 + echo "$type failed: retry in verbose mode" 1>&2 + $cmd_prefix -vvv ${cmd##$cmd_prefix} + status=1 + else + echo "" 1>&2 + echo "$type successful" 1>&2 + fi + echo "check_service - status: $status" 1>&2 + return $status +} +export APP="http://127.0.0.1:8080" + +echo "=== Test App ===" +set +e +exit_status=0 +ping_test_output="$(check_service "Ping" "curl -sSLf -X GET $APP/ping")" +ping_test=$? + +create_service_output="$(check_service "Create" "curl -sSLf -X POST $APP/service/mysql/$DATABASE_NAME-service/foo -d 'bar'")" +create_service=$? + +#get_service_output="$(check_service "Get" "curl -sSLf -X GET $APP/foo" )" +#get_service=$? + +get_service_output="$(check_service "Delete" "curl -sSLf -X GET $APP/service/mysql/$DATABASE_NAME-service/foo")" +get_service=$? + +set -e + +exit_status=$(($create_service + $get_service + $ping_test)) +echo "exit status: $exit_status" +echo "======================================================================================================" +echo "Dumping logs using docker logs cnb-app-container" +docker logs cnb-app-container 2>&1 +ls -lrt *.log + +if [ $exit_status -ne 0 ];then + echo "-------------------------------------------------------------------------------------------------" + echo "Dumping service logs" + if [ "$DEBUG" = 0 ];then + docker logs $SERVICE_NAME-service -n 30 2>&1 + else + docker logs $SERVICE_NAME-service 2>&1 + fi + +fi +exit $exit_status diff --git a/setup-prerequisite.sh b/setup-prerequisite.sh new file mode 100755 index 0000000..1984063 --- /dev/null +++ b/setup-prerequisite.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -e +echo "Processing $0" +SERVICE_ADMIN_PASSWORD="$SERVICE_PASSWORD-admin" +service_container_name="$SERVICE_NAME-service" +service_container_id=$(docker run -d --rm -p "$SERVICE_PORT:$SERVICE_PORT" \ + -e MARIADB_USER=$SERVICE_USERNAME \ + -e MARIADB_PASSWORD=$SERVICE_PASSWORD \ + -e MARIADB_DATABASE="$DATABASE_NAME" \ + -e MARIADB_ROOT_PASSWORD="$SERVICE_ADMIN_PASSWORD" \ + --name "$service_container_name" \ + --health-cmd "$SERVICE_HEALTH_CMD" \ + --health-interval 10s --health-timeout 5s --health-retries 5 \ + ${SERVICE_IMAGE} --port $SERVICE_PORT) + +docker logs -f "$service_container_name" &> "$service_container_name.log" & +echo "Waiting for $service_container_name" +while [ "$( docker container inspect -f '{{.State.Status}}' $service_container_name )" != "running" ]; do + echo "waiting for $service_container_name to be running, currently: $(docker inspect -f '{{.State.Status}}' $service_container_name)" + sleep 1 +done +while [ "$( docker container inspect -f '{{.State.Health.Status}}' $service_container_name )" != "healthy" ]; do + echo "waiting for $service_container_name to be healthy, currently: $(docker inspect -f '{{.State.Health.Status}}' $service_container_name)" + sleep 1 +done + +if [ -z "$service_container_id" ];then + echo "ERROR: failed to start container '$service_container_name' using $SERVICE_IMAGE" +else + echo "'$service_container_name' is running $SERVICE_IMAGE" +fi +service_container_name="$(docker ps -f "ancestor=$SERVICE_IMAGE" --format "{{.Names}}")" +SERVICE_CONTAINER_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$service_container_name") +echo "SERVICE_CONTAINER_IP: $SERVICE_CONTAINER_IP" +export SERVICE_HOST=$SERVICE_CONTAINER_IP +docker ps +echo "$SERVICE_NAME - MariaDb version in $service_container_name: $(docker exec -i $service_container_name mariadb --version)" + +echo "$SERVICE_NAME - Connection to database as Root: docker exec -i $service_container_name bash -c \"mariadb-admin --password='$SERVICE_ADMIN_PASSWORD' --host localhost --port $SERVICE_PORT ping\"" +docker exec -i $service_container_name bash -c "mariadb-admin --password='$SERVICE_ADMIN_PASSWORD' --host localhost --port $SERVICE_PORT ping" +echo "$SERVICE_NAME - Connection to database as User: docker exec -i $service_container_name bash -c \"mariadb --user $SERVICE_USERNAME --password='$SERVICE_PASSWORD' --host localhost --port $SERVICE_PORT $DATABASE_NAME -e 'SELECT DATABASE();'\"" +docker exec -i $service_container_name bash -c "mariadb --user $SERVICE_USERNAME --password='$SERVICE_PASSWORD' --host localhost --port $SERVICE_PORT $DATABASE_NAME -e 'SELECT DATABASE();'" + +echo "SERVICE_HOST: $SERVICE_HOST" diff --git a/start-cnd-app-locally.sh b/start-cnd-app-locally.sh new file mode 100755 index 0000000..11ae9f0 --- /dev/null +++ b/start-cnd-app-locally.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -e + +source local.env + +echo "Cleanup existing services" +for c in cnb-app-container $SERVICE_NAME-service;do + echo "Stopping $c" + if docker container stop $c;then + echo "Removing $c" + docker container rm -f $c + fi +done + +source ./setup-prerequisite.sh +./run-tests.sh + + + +#./vcap-services-template-reformat.sh >vcap-service.env +#cat vcap-service.env +#echo "" +#echo "Starting app using ${CNB_IMAGE_NAME}" +##docker run -it --rm -e PORT=8081 --env-file vcap-service.env -p 8080:8081 --name "cnb-app" ${CNB_IMAGE_NAME} +#docker run --rm -e PORT=80 --env-file vcap-service.env -p 8080:80 --name "cnb-app" ${CNB_IMAGE_NAME} +##docker run -d --rm -e PORT=8081 --env-file vcap-service.env -p 8080:8081 --name "cnb-app" ${CNB_IMAGE_NAME} \ No newline at end of file diff --git a/vcap-services-template-reformat.sh b/vcap-services-template-reformat.sh new file mode 100755 index 0000000..0c9e8b6 --- /dev/null +++ b/vcap-services-template-reformat.sh @@ -0,0 +1 @@ +echo VCAP_SERVICES="$(cat vcap-services-template.json|envsubst|jq -c '.')" > vcap-service.env \ No newline at end of file diff --git a/vcap-services-template.json b/vcap-services-template.json new file mode 100644 index 0000000..ec9e46c --- /dev/null +++ b/vcap-services-template.json @@ -0,0 +1,31 @@ +{ + "mariadb-dedicated": [ + { + "binding_guid": "6c38cfe4-1ca4-479a-a1be-9f3e41f16af0", + "binding_name": null, + "credentials": { + "hostname": "${SERVICE_HOST}", + "jdbcUrl": "jdbc:mysql://${SERVICE_HOST}:${SERVICE_PORT}/${DATABASE_NAME}?user=${SERVICE_USERNAME}&password=${SERVICE_PASSWORD}&useSSL=true&enabledTLSProtocols=TLSv1.2&enabledSslProtocolSuites=TLSv1.2", + "name": "${DATABASE_NAME}", + "password": "${SERVICE_PASSWORD}", + "uri": "mysql://${SERVICE_USERNAME}:${SERVICE_PASSWORD}@${SERVICE_HOST}:${SERVICE_PORT}/${DATABASE_NAME}?reconnect=true", + "username": "${SERVICE_USERNAME}", + "port": ${SERVICE_PORT} + }, + "instance_guid": "bd0fb925-6d4d-4cb5-b5ff-87cb23687937", + "instance_name": "cf-mysql-smoketest-1727438778", + "label": "mariadb-dedicated", + "name": "${DATABASE_NAME}-service", + "plan": "medium", + "provider": null, + "syslog_drain_url": null, + "tags": [ + "mysql" + ], + "volume_mounts": [] + } + ] +} + + +