diff --git a/.github/workflows/azure-crossplane-mongodb-e2e.yml b/.github/workflows/azure-crossplane-mongodb-e2e.yml
deleted file mode 100644
index bf97168..0000000
--- a/.github/workflows/azure-crossplane-mongodb-e2e.yml
+++ /dev/null
@@ -1,81 +0,0 @@
-name: MongoDB E2E Crossplane
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-on:
- pull_request:
- types: [opened, ready_for_review, reopened, synchronize]
- branches: main
- paths:
- - 'azure/crossplane/mongodb/**'
-
-env:
- PACKAGE_PROVIDER: azure
- PACKAGE_NAME: mongodb
-
-jobs:
- pre-publish:
- uses: ./.github/workflows/crossplane-prepublish.yml
-
- publish:
- needs:
- - pre-publish
- uses: ./.github/workflows/crossplane-publish.yml
- with:
- package: "mongodb"
- provider: "azure"
- version: ${{ needs.pre-publish.outputs.version }}
-
- test:
- runs-on: ubuntu-latest
- needs:
- - pre-publish
- - publish
- env:
- CONFIG_VERSION: ${{ needs.pre-publish.outputs.version }}
- CONFIG_IMAGE: ${{ needs.publish.outputs.package_registry }}/${{ needs.publish.outputs.package_repository }}
-
- steps:
- - name: Checkout
- uses: actions/checkout@v2
-
- - name: Install UXP
- run: |
- curl -sL "https://cli.upbound.io" | sh
- sudo mv up /usr/local/bin/
- up --version
-
- - name: Create k8s Kind Cluster
- uses: helm/kind-action@v1.4.0
- with:
- verbosity: 5
- version: "v0.16.0"
- kubectl_version: "v1.24.6"
- node_image: kindest/node:v1.24.6@sha256:97e8d00bc37a7598a0b32d1fabd155a96355c49fa0d4d4790aab0f161bf31be1
-
- - name: Verify Cluster
- run: |
- kubectl version
- which kubectl
- kubectl cluster-info
- kubectl get storageclass standard
-
- - name: Create Azure Config Secret
- run: |
- kubectl create namespace upbound-system
- kubectl create secret generic azure-secret -n upbound-system --from-literal=creds='${{ secrets.AZURE_CONFIG }}'
-
- - name: Test Crossplane MongoDB Package
- run: |
- pushd scripts
- ./crossplane-e2e-mongodb.sh
- popd
-
- - name: Cleanup MongoDB Package
- if: always()
- run: |
- pushd scripts
- ./mongodb/crossplane-e2e-cleanup.sh
- popd
diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml
index af80d60..162597f 100644
--- a/.github/workflows/build-docs.yml
+++ b/.github/workflows/build-docs.yml
@@ -1,4 +1,4 @@
-name: Deploy gh-pages
+name: Build docs
on:
pull_request:
@@ -18,12 +18,12 @@ jobs:
fetch-depth: '0'
- name: Setup Python
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v4
with:
python-version: 3.x
- name: Install Python requirements
run: pip install -r requirements.txt
- - name: Deploy GitHub Pages
+ - name: Build documentation
run: mkdocs build
diff --git a/.github/workflows/crossplane-main.yml b/.github/workflows/crossplane-main.yml
deleted file mode 100644
index 8970ea4..0000000
--- a/.github/workflows/crossplane-main.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-name: Publish workflow
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
-
-env:
- PACKAGE_REGISTRY: ghcr.io
- PACKAGE_REPOSITORY_BASE: ${{ github.repository }}
-
-on:
- push:
- branches:
- - main
- paths:
- - 'amazon/crossplane/**'
- - 'azure/crossplane/**'
- - 'google/crossplane/**'
-
-jobs:
- pre-publish:
- uses: ./.github/workflows/crossplane-prepublish.yml
-
- debug:
- runs-on: ubuntu-latest
- needs:
- - pre-publish
- steps:
- - run:
- echo "packages='${{ needs.pre-publish.outputs.packages }}'"
- echo "packages_fromJson='${{ fromJson(needs.pre-publish.outputs.packages) }}'"
-
- publish:
- needs:
- - pre-publish
- - debug
- uses: ./.github/workflows/crossplane-publish.yml
- strategy:
- matrix: ${{ fromJson(needs.pre-publish.outputs.packages) }}
- with:
- package: ${{ matrix.PACKAGE_NAME }}
- provider: ${{ matrix.PACKAGE_PROVIDER }}
- version: ${{ needs.pre-publish.outputs.version }}
diff --git a/.github/workflows/crossplane-prepublish.yml b/.github/workflows/crossplane-prepublish.yml
deleted file mode 100644
index 0539721..0000000
--- a/.github/workflows/crossplane-prepublish.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: Reusable pre-publish workflow
-
-on:
-
- workflow_call:
- outputs:
- version:
- value: ${{ jobs.pre-publish.outputs.version }}
- description: The new published version
- packages:
- value: ${{ jobs.pre-publish.outputs.packages }}
-
-jobs:
- pre-publish:
- runs-on: ubuntu-latest
- outputs:
- packages: ${{ steps.list.outputs.packages }}
- version: ${{ steps.version_bump.outputs.new_tag }}
- steps:
- - name: Checkout
- uses: actions/checkout@v3
- with:
- fetch-depth: '0'
-
- - name: Bump version and push tag
- id: version_bump
- uses: anothrNick/github-tag-action@v1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- WITH_V: false
- INITIAL_VERSION: 0.1.0
- RELEASE_BRANCHES: main
- PRERELEASE: true
- DEFAULT_BUMP: patch
-
- - name: List Packages That Have Changed
- id: list
- run: |
- diffs=$(git diff-tree --no-commit-id --name-only -r --diff-filter=ACM HEAD~1 HEAD | grep -E '^[^/]+/crossplane/' | cut -d/ -f-3 | jq -ncrR '[inputs]|unique')
- echo "diffs=${diffs}"
- packages=$(jq -nc --argjson diffs "${diffs}" '$diffs|map(split("/")|{"PACKAGE_PROVIDER":.[0],"PACKAGE_NAME":.[2]}) | { "include": . }')
- echo "packages=${packages}"
- echo "packages=${packages}" >> $GITHUB_OUTPUT
diff --git a/.github/workflows/crossplane-publish.yml b/.github/workflows/crossplane-publish.yml
deleted file mode 100644
index 28d50f6..0000000
--- a/.github/workflows/crossplane-publish.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-name: Reusable publish workflow
-
-
-on:
- workflow_call:
- inputs:
- version:
- type: string
- required: true
- package:
- type: string
- required: true
- provider:
- type: string
- required: true
- outputs:
- package_repository:
- value: ${{ jobs.publish.outputs.package_repository }}
- package_registry:
- value: ${{ jobs.publish.outputs.package_registry }}
-
-jobs:
-
- publish:
- runs-on: ubuntu-latest
-
- outputs:
- package_repository: ${{ env.PACKAGE_REPOSITORY }}
- package_registry: ${{ env.PACKAGE_REGISTRY }}
-
- env:
- PACKAGE_VERSION: ${{ inputs.version }}
- PACKAGE_REGISTRY: ghcr.io
- PACKAGE_REPOSITORY: ${{ github.repository }}/${{ inputs.provider }}/crossplane/${{ inputs.package }}
- PACKAGE_PROVIDER: ${{ inputs.provider }}
- PACKAGE_NAME: ${{ inputs.package }}
-
- steps:
- - name: Checkout
- uses: actions/checkout@v3
- with:
- ref: ${{ inputs.version }}
-
- - name: Install UXP
- run: |
- curl -sL "https://cli.upbound.io" | sh
- sudo mv up /usr/local/bin/
- up --version
-
- - name: Install Carvel tools
- uses: vmware-tanzu/carvel-setup-action@v1
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
- only: ytt
-
- - name: Log in to ${{ env.PACKAGE_REGISTRY }}
- uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 # v1.10.0
- with:
- registry: ${{ env.PACKAGE_REGISTRY }}
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Dump info
- run: |
- env | sort
-
- - name: Crossplane Package Publish
- run: |
- make crossplane-build
- make crossplane-push
diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml
index 967fe3b..792d5e2 100644
--- a/.github/workflows/publish-docs.yml
+++ b/.github/workflows/publish-docs.yml
@@ -1,4 +1,8 @@
-name: Deploy gh-pages
+name: Publish docs
+
+permissions:
+ pages: write
+ contents: write
on:
push:
@@ -18,12 +22,12 @@ jobs:
fetch-depth: '0'
- name: Setup Python
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v4
with:
python-version: 3.x
- name: Install Python requirements
run: pip install -r requirements.txt
- - name: Deploy GitHub Pages
+ - name: Build documentation and deploy GitHub Pages
run: mkdocs gh-deploy -b gh-pages
diff --git a/.github/workflows/publish-packages.yml b/.github/workflows/publish-packages.yml
new file mode 100644
index 0000000..33c2ff5
--- /dev/null
+++ b/.github/workflows/publish-packages.yml
@@ -0,0 +1,105 @@
+name: Publish packages
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ packages: write
+ contents: write
+ pull-requests: write
+
+on:
+ push:
+ branches:
+ - main
+ - develop
+ paths:
+ - 'packages/*/*/*/**'
+
+ pull_request:
+ types: [opened, ready_for_review, reopened, synchronize]
+ branches:
+ - main
+ paths:
+ - 'packages/*/*/*/**'
+
+jobs:
+
+ list-packages:
+ uses: ./.github/workflows/reusable-list-packages.yml
+ with:
+ basedir: packages
+
+ bump-version:
+ needs:
+ - list-packages
+ if: needs.list-packages.outputs.crossplane_publish == 'true' || needs.list-packages.outputs.carvel_publish == 'true'
+ uses: ./.github/workflows/reusable-bump-version.yml
+
+ crossplane-publish:
+ needs:
+ - bump-version
+ - list-packages
+ if: needs.list-packages.outputs.crossplane_publish == 'true'
+ strategy:
+ matrix: ${{ fromJson(needs.list-packages.outputs.crossplane) }}
+ uses: ./.github/workflows/reusable-crossplane-publish.yml
+ with:
+ package_name: ${{ matrix.name }}
+ package_provider: ${{ matrix.provider }}
+ package_version: ${{ needs.bump-version.outputs.version }}
+ packages_basedir: ${{ needs.list-packages.outputs.basedir }}
+ package_path: ${{ matrix.path }}
+ run-test: ${{ github.event_name == 'pull_request' }}
+ secrets: inherit
+
+ carvel-publish-package:
+ needs:
+ - bump-version
+ - list-packages
+ if: needs.list-packages.outputs.carvel_publish == 'true'
+ strategy:
+ matrix: ${{ fromJson(needs.list-packages.outputs.carvel) }}
+ uses: ./.github/workflows/reusable-carvel-publish-package.yml
+ with:
+ package_name: ${{ matrix.name }}
+ package_provider: ${{ matrix.provider }}
+ package_version: ${{ needs.bump-version.outputs.version }}
+ packages_basedir: ${{ needs.list-packages.outputs.basedir }}
+ package_path: ${{ matrix.path }}
+
+ carvel-publish-repo:
+ if: needs.list-packages.outputs.carvel_publish == 'true'
+ uses: ./.github/workflows/reusable-carvel-publish-repo.yml
+ needs:
+ - list-packages
+ - bump-version
+ - carvel-publish-package
+ with:
+ packages_basedir: ${{ needs.list-packages.outputs.basedir }}
+ packages_list: ${{ needs.list-packages.outputs.carvel }}
+ package_version: ${{ needs.bump-version.outputs.version }}
+ repo_version: ${{ needs.bump-version.outputs.version }}
+ prepare_repo_pr: ${{ needs.bump-version.outputs.is_prerelease == 'false' }}
+ release: ${{ needs.bump-version.outputs.is_prerelease == 'true' }}
+
+ carvel-test:
+ if: github.event_name == 'pull_request'
+ uses: ./.github/workflows/reusable-carvel-test.yml
+ needs:
+ - list-packages
+ - bump-version
+ - carvel-publish-repo
+ strategy:
+ matrix: ${{ fromJson(needs.list-packages.outputs.carvel) }}
+ with:
+ repo_version: ${{ needs.bump-version.outputs.version }}
+ package_name: ${{ matrix.name }}
+ package_provider: ${{ matrix.provider }}
+ package_version: ${{ needs.bump-version.outputs.version }}
+ packages_basedir: ${{ needs.list-packages.outputs.basedir }}
+ package_path: ${{ matrix.path }}
+ pull_request_number: ${{ needs.carvel-publish-repo.outputs.pull_request_number}}
+ package_prerelease: ${{ needs.bump-version.outputs.is_prerelease }}
+ secrets: inherit
diff --git a/.github/workflows/reusable-bump-version.yml b/.github/workflows/reusable-bump-version.yml
new file mode 100644
index 0000000..ba869c5
--- /dev/null
+++ b/.github/workflows/reusable-bump-version.yml
@@ -0,0 +1,58 @@
+name: Reusable bump-version workflow
+
+on:
+
+ workflow_call:
+
+ inputs:
+ default_bump:
+ type: string
+ default: patch
+
+ outputs:
+ version:
+ value: ${{ jobs.bump-version.outputs.version }}
+ description: The new published version
+ is_prerelease:
+ value: ${{ jobs.bump-version.outputs.is_prerelease }}
+ description: Whether the version is a pre-release or not
+
+jobs:
+
+ bump-version:
+
+ runs-on: ubuntu-latest
+
+ outputs:
+ version: ${{ steps.bump.outputs.new_tag }}
+ is_prerelease: ${{ steps.pre.outputs.is_prerelease }}
+
+ steps:
+
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Define default branch
+ run: |
+ if [[ "${{ github.event_name }}" == "pull_request" ]]; then
+ echo "DEFAULT_BRANCH=${{ github.head_ref }}" >> $GITHUB_ENV
+ fi
+
+ - name: Bump version and push tag
+ id: bump
+ uses: anothrNick/github-tag-action@1.57.0
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ WITH_V: false
+ INITIAL_VERSION: 0.1.0
+ RELEASE_BRANCHES: main
+ PRERELEASE: true
+ DEFAULT_BUMP: ${{ inputs.default_bump }}
+
+ - name: Check pre-release
+ id: pre
+ run: |
+ [[ "$TAG" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && is_prerelease=false || is_prerelease=true
+ echo "is_prerelease=${is_prerelease}" >> $GITHUB_OUTPUT
+ env:
+ TAG: ${{ steps.bump.outputs.new_tag }}
\ No newline at end of file
diff --git a/.github/workflows/reusable-carvel-publish-package.yml b/.github/workflows/reusable-carvel-publish-package.yml
new file mode 100644
index 0000000..cdcfce8
--- /dev/null
+++ b/.github/workflows/reusable-carvel-publish-package.yml
@@ -0,0 +1,103 @@
+name: Reusable workflow for publishing Carvel packages
+
+on:
+
+ workflow_call:
+
+ inputs:
+ packages_basedir:
+ type: string
+ description: Packages base directory path
+ default: packages
+ package_path:
+ type: string
+ required: true
+ package_version:
+ type: string
+ required: true
+ package_name:
+ type: string
+ required: true
+ package_provider:
+ type: string
+ required: true
+ repo_dir:
+ type: string
+ description: Directory path to hold the contents of the Carvel repository
+ default: repository
+ kctrl_version:
+ type: string
+ default: v0.43.2
+
+ outputs:
+ package_repository:
+ value: ${{ jobs.publish.outputs.package_repository }}
+ package_registry:
+ value: ${{ jobs.publish.outputs.package_registry }}
+ package_metadata_name:
+ value: ${{ jobs.publish.outputs.package_metadata_name }}
+
+env:
+ PACKAGE_VERSION: ${{ inputs.package_version }}
+ PACKAGE_REGISTRY: ghcr.io
+ PACKAGE_REPOSITORY: ${{ github.repository }}/${{ inputs.package_provider }}/carvel/${{ inputs.package_name }}
+ PACKAGE_PROVIDER: ${{ inputs.package_provider }}
+ PACKAGE_NAME: ${{ inputs.package_name }}
+ PACKAGES_BASEDIR: ${{ inputs.packages_basedir }}
+ PACKAGE_DIR: ${{ inputs.packages_basedir }}/${{ inputs.package_path }}
+
+ CARVEL_REPO_DIR: ${{ inputs.repo_dir }}
+
+jobs:
+
+ publish:
+ runs-on: ubuntu-latest
+
+ outputs:
+ package_repository: ${{ env.PACKAGE_REPOSITORY }}
+ package_registry: ${{ env.PACKAGE_REGISTRY }}
+ package_metadata_name: ${{ steps.info.outputs.package_metadata_name }}
+
+ steps:
+
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: '0'
+
+ - name: Install Carvel tools
+ uses: vmware-tanzu/carvel-setup-action@v1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ kctrl: ${{ inputs.kctrl_version }}
+
+ - name: Kctrl Release
+ run: make kctrl-release
+ env:
+ IMGPKG_REGISTRY_HOSTNAME: ${{ env.PACKAGE_REGISTRY }}
+ IMGPKG_REGISTRY_USERNAME: ${{ github.actor }}
+ IMGPKG_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Get package info
+ id: info
+ run: |
+ package_metadata_name=$(yq e '.metadata.name' ${PACKAGE_DIR}/package-metadata.yml)
+ echo "package_metadata_name=${package_metadata_name}" >> $GITHUB_OUTPUT
+
+ # The files for the repository have been generated into ${CARVEL_REPO_DIR}/packages
+ # They will be added to a dedicated branch
+ - name: Push artifacts
+ run: |
+ CURRENT_BRANCH=$(git branch --show-current)
+ ARTIFACTS_BRANCH_NAME=${PACKAGE_METADATA_NAME}/${PACKAGE_VERSION}
+ ARTIFACTS_DIR=${CARVEL_REPO_DIR}/packages/${PACKAGE_METADATA_NAME}
+
+ git config --global user.email "${{ github.event.repository.name }}@${{ github.repository_owner }}.github.io"
+ git config --global user.name "GitHub Actions Workflow"
+
+ git checkout -b ${ARTIFACTS_BRANCH_NAME}
+ git add ${ARTIFACTS_DIR}
+ git commit -m "${ARTIFACTS_BRANCH_NAME}"
+ git push -u origin ${ARTIFACTS_BRANCH_NAME}
+ env:
+ PACKAGE_METADATA_NAME: ${{ steps.info.outputs.package_metadata_name }}
diff --git a/.github/workflows/reusable-carvel-publish-repo.yml b/.github/workflows/reusable-carvel-publish-repo.yml
new file mode 100644
index 0000000..b7a24e2
--- /dev/null
+++ b/.github/workflows/reusable-carvel-publish-repo.yml
@@ -0,0 +1,170 @@
+name: Reusable workflow for publishing Carvel packages repository
+
+on:
+
+ workflow_call:
+
+ inputs:
+ packages_basedir:
+ type: string
+ description: Packages base directory path
+ default: packages
+ packages_list:
+ type: string
+ description: List of packages that have been changed
+ required: true
+ repo_dir:
+ type: string
+ default: repository
+ release:
+ type: boolean
+ default: false
+ repo_name:
+ type: string
+ default: carvel-reference-packages
+ repo_version:
+ type: string
+ required: true
+ prepare_repo_pr:
+ type: boolean
+ default: false
+ package_version:
+ type: string
+ required: true
+ kctrl_version:
+ type: string
+ default: v0.43.2
+
+ outputs:
+ registry:
+ value: ${{ jobs.publish.outputs.registry }}
+ repository:
+ value: ${{ jobs.publish.outputs.repository }}
+ pull_request_number:
+ value: ${{ jobs.prepare.outputs.pull_request_number }}
+
+env:
+ CARVEL_REPO_DIR: ${{ inputs.repo_dir }}
+ CARVEL_REPO_NAME: ${{ inputs.repo_name }}
+ CARVEL_REPO_REGISTRY: ghcr.io
+ CARVEL_REPO_REPOSITORY: ${{ github.repository }}/${{ inputs.repo_name }}
+ CARVEL_REPO_VERSION: ${{ inputs.repo_version }}
+ REPO_BRANCH_NAME: carvel-repo-${{ inputs.repo_version }}
+ PACKAGE_VERSION: ${{ inputs.package_version }}
+
+jobs:
+
+ prepare:
+ runs-on: ubuntu-latest
+
+ outputs:
+ pull_request_number: ${{ steps.pr.outputs.PULL_REQUEST_NUMBER }}
+
+ steps:
+
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: '0'
+
+ - name: Prepare git client
+ id: git
+ run: |
+ git config --global user.email "${{ github.event.repository.name }}@${{ github.repository_owner }}.github.io"
+ git config --global user.name "GitHub Actions Workflow"
+
+ sleep 5 && git fetch --all
+
+ # get the last token splitting by / to get the branch name, as in refs/heads/main
+ echo "CURRENT_BRANCH=$(grep -oE '[^/]+$' <<<$REF)" | tee -a $GITHUB_ENV
+ env:
+ REF: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref }}
+
+ - name: Create branch
+ id: branch
+ run: |
+ # make sure the head branch is checked out on pull_request events
+ git checkout ${CURRENT_BRANCH}
+
+ # create new branch
+ git checkout -b ${REPO_BRANCH_NAME}
+
+ # include the changes from all the packages' branches
+ # and remove them at the end of the script
+ BRANCHES=
+ for PACKAGE_DIR in $(jq -r '.include[]|.path' <<<$PACKAGES_LIST); do
+ PACKAGE_METADATA_NAME=$(yq e '.metadata.name' ${PACKAGES_BASEDIR}/${PACKAGE_DIR}/package-metadata.yml)
+ PACKAGE_BRANCH="${PACKAGE_METADATA_NAME}/${PACKAGE_VERSION}"
+ BRANCHES="${BRANCHES} ${PACKAGE_BRANCH}"
+ git rebase origin/${PACKAGE_BRANCH}
+ done
+ trap "tree -a repository ; git push --delete origin ${BRANCHES}" EXIT
+
+ # prepare a proper commit message
+ COMMIT_MESSAGE="$(git log --pretty=format:'%s' ${CURRENT_BRANCH}..HEAD)"
+
+ # discard the existing commits from packages' branches but do keep the changed files
+ git reset --soft ${CURRENT_BRANCH}
+
+ # create a new commit on the repo branch and push it to origin
+ git add . && git commit -m "${COMMIT_MESSAGE}" && git push -u origin ${REPO_BRANCH_NAME}
+ env:
+ PACKAGES_BASEDIR: ${{ inputs.packages_basedir }}
+ PACKAGES_LIST: ${{ inputs.packages_list }}
+
+ - name: Create pull request
+ id: pr
+ run: |
+ if [[ "${{ github.event_name }}" == "pull_request" ]]; then
+ echo "PULL_REQUEST_NUMBER=${{ github.event.number }}" >> $GITHUB_OUTPUT
+ elif [[ "${{ inputs.prepare_repo_pr }}" =~ ^[tT][rR][uU][eE]$ ]]; then
+ COMMIT_MESSAGE="$(git log --pretty=format:'%s' HEAD~..HEAD)"
+
+ # create the pull request against CURRENT_BRANCH
+ gh pr create --base ${CURRENT_BRANCH} --title "Carvel repository release at commit ${{ github.sha }}" --body "${COMMIT_MESSAGE}"
+
+ # get pull request number
+ echo "PULL_REQUEST_NUMBER=$(gh pr view --json number -q '.number' ${REPO_BRANCH_NAME})" >> $GITHUB_OUTPUT
+ fi
+ env:
+ GH_TOKEN: ${{ github.token }}
+
+ publish:
+ runs-on: ubuntu-latest
+ needs:
+ - prepare
+
+ outputs:
+ registry: ${{ env.CARVEL_REPO_REGISTRY }}
+ repository: ${{ env.CARVEL_REPO_REPOSITORY }}
+
+ steps:
+
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ ref: ${{ env.REPO_BRANCH_NAME }}
+
+ - name: Install Carvel tools
+ uses: vmware-tanzu/carvel-setup-action@v1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ kctrl: ${{ inputs.kctrl_version }}
+
+ - name: Kctrl Release
+ id: kctrl
+ run: |
+ make kctrl-repo-release
+ env:
+ IMGPKG_REGISTRY_HOSTNAME: ${{ env.CARVEL_REPO_REGISTRY }}
+ IMGPKG_REGISTRY_USERNAME: ${{ github.actor }}
+ IMGPKG_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Release
+ if: inputs.release
+ uses: softprops/action-gh-release@v1
+ with:
+ tag_name: ${{ env.CARVEL_REPO_VERSION }}
+ fail_on_unmatched_files: true
+ files: |
+ repository/package-repository.yml
diff --git a/.github/workflows/reusable-carvel-test.yml b/.github/workflows/reusable-carvel-test.yml
new file mode 100644
index 0000000..17b21e5
--- /dev/null
+++ b/.github/workflows/reusable-carvel-test.yml
@@ -0,0 +1,169 @@
+name: Reusable workflow for testing Carvel packages
+
+on:
+
+ workflow_call:
+
+ inputs:
+ repo_version:
+ type: string
+ required: true
+ package_version:
+ type: string
+ required: true
+ package_prerelease:
+ type: string
+ required: true
+ package_name:
+ type: string
+ required: true
+ package_provider:
+ type: string
+ required: true
+ packages_basedir:
+ type: string
+ description: Packages base directory path
+ default: packages
+ package_path:
+ type: string
+ required: true
+ pull_request_number:
+ type: string
+ required: true
+ kubernetes_version:
+ type: string
+ default: v1.24.6
+ kind_version:
+ type: string
+ default: v0.16.0
+ kapp_controller_version:
+ type: string
+ default: v0.43.2
+ secretgen_controller_version:
+ type: string
+ default: v0.12.0
+
+ secrets:
+ AZURE_CONFIG:
+ required: false
+
+jobs:
+
+ test:
+ runs-on: ubuntu-latest
+
+ env:
+ PACKAGE_VERSION: ${{ inputs.package_version }}
+
+ steps:
+
+ - name: Install Carvel tools
+ uses: vmware-tanzu/carvel-setup-action@v1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Create k8s Kind Cluster
+ uses: helm/kind-action@v1.4.0
+ with:
+ verbosity: 5
+ version: ${{ inputs.kind_version }}
+ kubectl_version: ${{ inputs.kubernetes_version }}
+ node_image: kindest/node:${{ inputs.kubernetes_version }}
+
+ - name: Verify Cluster
+ run: |
+ kubectl version
+ which kubectl
+ kubectl cluster-info
+ kubectl get storageclass standard
+
+ - name: Install kapp-controller
+ run: |
+ MANIFEST="https://github.com/vmware-tanzu/carvel-kapp-controller/releases/download/${{ inputs.kapp_controller_version }}/release.yml"
+ if [[ "${{ inputs.kapp_controller_version }}" == "latest" ]]; then
+ MANIFEST="https://github.com/vmware-tanzu/carvel-kapp-controller/releases/latest/download/release.yml"
+ fi
+
+ kubectl apply -f ${MANIFEST}
+ time kubectl wait --for=condition=Available=True -n kapp-controller deploy kapp-controller
+ time kubectl wait --for=condition=Available=True apiservices.apiregistration.k8s.io v1alpha1.data.packaging.carvel.dev
+
+ # determine the kapp-controller global namespace
+ echo KAPP_GLOBAL_NAMESPACE=$(kubectl -n kapp-controller get deployment kapp-controller -o json | jq -r '.spec.template.spec.containers[]|select(.name=="kapp-controller").args[]|select(.|startswith("-packaging-global-namespace"))|split("=")[1]') >> $GITHUB_ENV
+
+ - name: Install secretgen-controller
+ run: |
+ MANIFEST="https://github.com/vmware-tanzu/carvel-secretgen-controller/releases/download/${{ inputs.secretgen_controller_version }}/release.yml"
+ if [[ "${{ inputs.secretgen_controller_version }}" == "latest" ]]; then
+ MANIFEST="https://github.com/vmware-tanzu/carvel-secretgen-controller/releases/latest/download/release.yml"
+ fi
+
+ kubectl apply -f ${MANIFEST}
+ time kubectl wait --for=condition=Available=True -n secretgen-controller deploy secretgen-controller
+ time kubectl wait --for=condition=Available=True apiservices.apiregistration.k8s.io v1alpha1.secretgen.carvel.dev
+
+ - name: Install Carvel repository
+ run: |
+ REPO_MANIFEST="https://github.com/${{ github.repository }}/releases/download/${{ inputs.repo_version }}/package-repository.yml"
+ REPO_MANIFEST_FILE=$(mktemp)
+ curl -sSfL -o ${REPO_MANIFEST_FILE} ${REPO_MANIFEST}
+ REPO_NAME=$(yq '.metadata.name' ${REPO_MANIFEST_FILE})
+ kubectl apply -n ${KAPP_GLOBAL_NAMESPACE} -f ${REPO_MANIFEST_FILE}
+ time kubectl wait --for=condition=ReconcileSucceeded=True packagerepositories.packaging.carvel.dev -n ${KAPP_GLOBAL_NAMESPACE} ${REPO_NAME}
+
+ - name: List available packages
+ run: |
+ kubectl get packages.data.packaging.carvel.dev
+
+ - name: Mask secret
+ if: inputs.package_provider == 'azure'
+ uses: matteo-magni/secret-mask-action@main
+ with:
+ secret: ${{ secrets.AZURE_CONFIG }}
+
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Test Carvel package
+ run: |
+ if [[ "${{ inputs.package_provider }}" == "azure" ]]; then
+ # transforms the keys in the input JSON secret from camelCase to UPPER_SNAKE_CASE with the AZURE_ prefix
+ # in order not to divert from the official ASO docs and the client JSON file generated by az CLI
+ for k in $(jq -r 'keys[]' <<<"$AZURE_CONFIG"); do
+ K=$(echo azure_$k | sed -r 's/([a-z0-9])([A-Z])/\1_\L\2/g' | tr '[:lower:]' '[:upper:]')
+ eval export $K=$(jq -r '.'$k <<<"$AZURE_CONFIG")
+ done
+ fi
+
+ export PACKAGE_METADATA_NAME=$(yq e '.metadata.name' ${PACKAGE_DIR}/package-metadata.yml)
+ echo PACKAGE_METADATA_NAME=${PACKAGE_METADATA_NAME} >> $GITHUB_ENV
+
+ if [[ "$PRERELEASE" =~ ^[tT][rR][uU][eE]$ ]]; then
+ if [ -x ${SCRIPT} ]; then
+ ${SCRIPT}
+ elif [ ! -z "$PULL_REQUEST_NUMBER" ]; then
+ # no pre-release test script provided
+ LABEL=untested
+ gh label create -c ffaa00 --force $LABEL
+ gh pr edit $PULL_REQUEST_NUMBER --add-label $LABEL
+ gh pr comment $PULL_REQUEST_NUMBER --body "WARNING - package ${PACKAGE_METADATA_NAME}/${PACKAGE_VERSION} has not been tested"
+ fi
+ else
+ ${SCRIPT}
+ fi
+ env:
+ SCRIPT: ./scripts/carvel-e2e-${{ inputs.package_provider }}-${{ inputs.package_name }}.sh
+ PACKAGE_DIR: ${{ inputs.packages_basedir }}/${{ inputs.package_path }}
+ PRERELEASE: ${{ inputs.package_prerelease }}
+ PULL_REQUEST_NUMBER: ${{ inputs.pull_request_number }}
+ GH_TOKEN: ${{ github.token }}
+
+ # Azure-specific variable
+ AZURE_CONFIG: ${{ secrets.AZURE_CONFIG }}
+
+ - name: Cleanup
+ if: always()
+ run: |
+ if [ -x ${SCRIPT} ]; then ${SCRIPT}; fi
+ env:
+ SCRIPT: ./scripts/carvel-e2e-${{ inputs.package_provider }}-${{ inputs.package_name }}/cleanup.sh
diff --git a/.github/workflows/reusable-crossplane-publish.yml b/.github/workflows/reusable-crossplane-publish.yml
new file mode 100644
index 0000000..eb45f49
--- /dev/null
+++ b/.github/workflows/reusable-crossplane-publish.yml
@@ -0,0 +1,100 @@
+name: Reusable workflow for publishing Crossplane packages
+
+on:
+
+ workflow_call:
+
+ inputs:
+ packages_basedir:
+ type: string
+ description: Packages base directory path
+ default: packages
+ package_path:
+ type: string
+ required: true
+ package_version:
+ type: string
+ required: true
+ package_name:
+ type: string
+ required: true
+ package_provider:
+ type: string
+ required: true
+ run-test:
+ type: boolean
+ default: false
+
+ outputs:
+ package_repository:
+ value: ${{ jobs.publish.outputs.package_repository }}
+ package_registry:
+ value: ${{ jobs.publish.outputs.package_registry }}
+
+ secrets:
+ AZURE_CONFIG:
+ required: false
+
+jobs:
+
+ publish:
+ runs-on: ubuntu-latest
+
+ outputs:
+ package_repository: ${{ env.PACKAGE_REPOSITORY }}
+ package_registry: ${{ env.PACKAGE_REGISTRY }}
+
+ env:
+ PACKAGE_VERSION: ${{ inputs.package_version }}
+ PACKAGE_REGISTRY: ghcr.io
+ PACKAGE_REPOSITORY: ${{ github.repository }}/${{ inputs.package_provider }}/crossplane/${{ inputs.package_name }}
+ PACKAGE_PROVIDER: ${{ inputs.package_provider }}
+ PACKAGE_NAME: ${{ inputs.package_name }}
+ PACKAGES_BASEDIR: ${{ inputs.packages_basedir }}
+ PACKAGE_DIR: ${{ inputs.packages_basedir }}/${{ inputs.package_path }}
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ ref: ${{ inputs.package_version }}
+
+ - name: Install up CLI
+ run: |
+ curl -sL "https://cli.upbound.io" | sh
+ sudo mv up /usr/local/bin/
+ up --version
+
+ - name: Install Carvel tools
+ uses: vmware-tanzu/carvel-setup-action@v1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ only: ytt
+
+ - name: Log in to ${{ env.PACKAGE_REGISTRY }}
+ uses: docker/login-action@v2.1.0
+ with:
+ registry: ${{ env.PACKAGE_REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Dump info
+ run: |
+ env | sort
+
+ - name: Crossplane Package Publish
+ run: |
+ make crossplane-push
+
+ test:
+ if: inputs.run-test
+ uses: ./.github/workflows/reusable-crossplane-test.yml
+ needs:
+ - publish
+ with:
+ package_version: ${{ inputs.package_version }}
+ package_name: ${{ inputs.package_name }}
+ package_provider: ${{ inputs.package_provider }}
+ package_registry: ${{ needs.publish.outputs.package_registry }}
+ package_repository: ${{ needs.publish.outputs.package_repository }}
+ secrets: inherit
diff --git a/.github/workflows/reusable-crossplane-test.yml b/.github/workflows/reusable-crossplane-test.yml
new file mode 100644
index 0000000..77565ad
--- /dev/null
+++ b/.github/workflows/reusable-crossplane-test.yml
@@ -0,0 +1,111 @@
+name: Reusable workflow for testing Crossplane packages
+
+
+on:
+
+ workflow_call:
+
+ inputs:
+ package_version:
+ type: string
+ required: true
+ package_name:
+ type: string
+ required: true
+ package_provider:
+ type: string
+ required: true
+ package_registry:
+ type: string
+ required: true
+ package_repository:
+ type: string
+ required: true
+ kubernetes_version:
+ type: string
+ default: v1.26.0
+ kind_version:
+ type: string
+ default: v0.17.0
+
+ secrets:
+ AZURE_CONFIG:
+ required: false
+jobs:
+
+ test:
+ runs-on: ubuntu-latest
+ env:
+ CROSSPLANE_NAMESPACE: upbound-system
+ CONFIG_VERSION: ${{ inputs.package_version }}
+ CONFIG_IMAGE: ${{ inputs.package_registry }}/${{ inputs.package_repository }}
+ UNIQUE_NAME: gh-${{ github.run_id }}-${{ github.run_attempt }}
+ STORAGE_CLASS: standard # default storage class for kind
+
+ steps:
+
+ - name: Get runner info
+ run: |
+ uname -a
+ top -bn1
+
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Install Carvel tools
+ uses: vmware-tanzu/carvel-setup-action@v1
+ with:
+ only: ytt
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Install up CLI
+ run: |
+ curl -sL "https://cli.upbound.io" | sh
+ sudo mv up /usr/local/bin/
+ up --version
+
+ - name: Create k8s Kind Cluster
+ uses: helm/kind-action@v1.5.0
+ with:
+ verbosity: 5
+ version: ${{ inputs.kind_version }}
+ kubectl_version: ${{ inputs.kubernetes_version }}
+ node_image: kindest/node:${{ inputs.kubernetes_version }}
+
+ - name: Verify Cluster
+ run: |
+ kubectl version
+ which kubectl
+ kubectl cluster-info
+ kubectl get storageclass standard
+
+ - name: Install Universal Crossplane
+ run: |
+ ./scripts/crossplane-install-uxp.sh ${UXP_VERSION}
+ env:
+ UXP_VERSION: v1.10.1-up.1
+
+ - name: Test Crossplane package
+ id: test
+ run: |
+ if [ -x ${SCRIPT} ]; then ${SCRIPT}; fi
+ env:
+ INSTALL_PROVIDER: "true"
+
+ SCRIPT: ./scripts/crossplane-e2e-${{ inputs.package_provider }}-${{ inputs.package_name }}.sh
+ CLAIM_NAME: ${{ env.UNIQUE_NAME }}
+ TEST_APP_NAME: ${{ env.UNIQUE_NAME }}
+ CONFIG_NAME: ${{ env.UNIQUE_NAME }}
+
+ # Azure-specific variable
+ AZURE_CONFIG: ${{ secrets.AZURE_CONFIG }}
+
+ - name: Cleanup
+ if: always()
+ run: |
+ if [ -x ${SCRIPT} ]; then ${SCRIPT}; fi
+ env:
+ SCRIPT: ./scripts/crossplane-e2e-${{ inputs.package_provider }}-${{ inputs.package_name }}/cleanup.sh
+ CLAIM_NAME: ${{ env.UNIQUE_NAME }}
+ TEST_APP_NAME: ${{ env.UNIQUE_NAME }}
+ CONFIG_NAME: ${{ env.UNIQUE_NAME }}
diff --git a/.github/workflows/reusable-list-packages.yml b/.github/workflows/reusable-list-packages.yml
new file mode 100644
index 0000000..cb2841c
--- /dev/null
+++ b/.github/workflows/reusable-list-packages.yml
@@ -0,0 +1,100 @@
+name: Reusable workflow for listing changed packages
+
+on:
+
+ workflow_call:
+
+ inputs:
+ basedir:
+ type: string
+ description: Packages base directory path
+ default: packages
+ ref:
+ type: string
+ description: Git reference to compare HEAD with
+ default: HEAD~1
+
+ outputs:
+ basedir:
+ value: ${{ inputs.basedir }}
+ all:
+ value: ${{ jobs.list-packages.outputs.all }}
+ crossplane:
+ value: ${{ jobs.list-packages.outputs.crossplane }}
+ carvel:
+ value: ${{ jobs.list-packages.outputs.carvel }}
+ crossplane_publish:
+ value: ${{ jobs.list-packages.outputs.crossplane_publish }}
+ carvel_publish:
+ value: ${{ jobs.list-packages.outputs.carvel_publish }}
+
+jobs:
+
+ list-packages:
+
+ runs-on: ubuntu-latest
+
+ outputs:
+ all: ${{ steps.list.outputs.packages }}
+ crossplane: ${{ steps.filter.outputs.crossplane }}
+ carvel: ${{ steps.filter.outputs.carvel }}
+ crossplane_publish: ${{ steps.filter.outputs.crossplane_publish }}
+ carvel_publish: ${{ steps.filter.outputs.carvel_publish }}
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: '0'
+
+ # Prepare a list of changed files between $GIT_REF (default: HEAD~1) and HEAD
+ # that match a specific pattern: BASEDIR/PROVIDER/PACKAGING/NAME/*
+ # BASEDIR is set to 'packages' by default
+ # PROVIDER can be the Cloud name ("aws", "azure", "gcp"), "multicloud" in case the package spans multiple clouds/platforms or free-form string (for example, "helm")
+ # PACKAGING can currently support either "crossplane" or "carvel"
+ # NAME is the package's name
+
+ # The `git diff-tree` command outputs a list of file paths starting from the root of the repo, one per line,
+ # that have been either Added, Copied, Modified or Renamed (ACMR).
+ # This list is fed to `jq` that transforms it into an actual json array, filters by the entries that start with "BASEDIR/"
+ # and strip the prefix.
+ # Each entry is then tokenised by /, morphed into an object with "provider" key set as the first token, "packaging" as the second one, "name" as the third one
+ # and the "path" as them all back together.
+ # The resulting array is then purged of the duplicates and then set as value for the "include" key in a new object,
+ # in order to meet https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#expanding-or-adding-matrix-configurations.
+ # The result is appended to GITHUB_OUTPUT as well as written to the workflow's output for debugging purposes (thus the `tee -a`).
+ - name: List packages that have changed
+ id: list
+ run: |
+ packages=$(git diff-tree --name-only -r --diff-filter=ACMR ${GIT_REF} HEAD | jq -ncrR --arg BASEDIR ${BASEDIR%%/} '
+ [inputs]
+ | map(capture("^"+$BASEDIR+"/(?
.*)$").p
+ | split("/")
+ | { "path": .[0:3]|join("/"), "provider": .[0], "packaging": .[1], "name": .[2] }
+ )
+ | map(select(.provider!=null and .packaging!=null and .name!=null))
+ | unique
+ | { "include": . }
+ ')
+ echo "packages=${packages}" | tee -a $GITHUB_OUTPUT
+ env:
+ BASEDIR: ${{ inputs.basedir }}
+ GIT_REF: ${{ inputs.ref }}
+
+ # Filter packages by packaging system, in order to feed them to different workflows for publishing and testing.
+ # The $PACKAGES variable is filled with the result from the previous step and filtered using `jq` based on the "packaging" key.
+ # Currently, only "crossplane" and "carvel" are supported and therefore only those outputs are provided, and contain
+ # the list of packages per packaging system that have changed (as value of the "include" key as before).
+ # Boolean outputs are also provided ("crossplane_publish" or "carvel_publish") to tell whether the above list has at least 1 item or not.
+ - name: Filter packages
+ id: filter
+ run: |
+ crossplane=$(jq -c '.include|=map(select(.packaging=="crossplane"))' <<< $PACKAGES)
+ echo "crossplane=${crossplane}" | tee -a $GITHUB_OUTPUT
+ echo "crossplane_publish=$(jq '.include|length > 0' <<<${crossplane})" | tee -a $GITHUB_OUTPUT
+
+ carvel=$(jq -c '.include|=map(select(.packaging=="carvel"))' <<< $PACKAGES)
+ echo "carvel=${carvel}" | tee -a $GITHUB_OUTPUT
+ echo "carvel_publish=$(jq '.include|length > 0' <<<${carvel})" | tee -a $GITHUB_OUTPUT
+ env:
+ PACKAGES: ${{ steps.list.outputs.packages }}
diff --git a/.gitignore b/.gitignore
index fa06fd1..de1f7ab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,106 @@
# Except this file
!.gitignore
+
/site/
+**/.src/
+**/.package/
+pkgrepo-build.yml
+
+# Created by https://www.toptal.com/developers/gitignore/api/macos,linux,windows,visualstudiocode
+# Edit at https://www.toptal.com/developers/gitignore?templates=macos,linux,windows,visualstudiocode
+
+### Linux ###
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### macOS Patch ###
+# iCloud generated files
+*.icloud
+
+### VisualStudioCode ###
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+!.vscode/*.code-snippets
+
+# Local History for Visual Studio Code
+.history/
+
+# Built Visual Studio Code Extensions
+*.vsix
+
+### VisualStudioCode Patch ###
+# Ignore all local history of files
+.history
+.ionide
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# End of https://www.toptal.com/developers/gitignore/api/macos,linux,windows,visualstudiocode
+
diff --git a/Makefile b/Makefile
index c067a89..bfb3d10 100644
--- a/Makefile
+++ b/Makefile
@@ -1,112 +1,41 @@
-LOCAL_VERSION = $(shell git describe --tags --always)
-PACKAGE_VERSION ?= "0.0.0-${LOCAL_VERSION}"
-PACKAGE_REPOSITORY ?= "vmware-tanzu-labs/trp-azure-psql"
-PACKAGE_REGISTRY ?= "ghcr.io"
-CLOUD_NAME = azure
+PACKAGES_BASEDIR ?= packages
+PACKAGE_DIR ?= ${PACKAGES_BASEDIR}/${PACKAGE_PROVIDER}/${PACKAGE_PACKAGING}/${PACKAGE_NAME}
-PACKAGES_DIR = ./packages
-PACKAGE_DIR = ${PACKAGES_DIR}/${PACKAGE_NAME}
-PACKAGE_BUILD_BASE_DIR = ./package-build
-PACKAGE_BUILD_DIR = ${PACKAGE_BUILD_BASE_DIR}/${PACKAGE_NAME}
-PACKAGE_METADATA_DIR = ${PACKAGE_DIR}/package-metadata
-PACKAGES_REPO_STAGING_DIR = ./packages-repo
-TEMPLATES_DIR = ${PACKAGE_DIR}/package-templates
+CARVEL_REPO_DIR ?= repository
-PACKAGE_TEST_DATA_DIR_NAME = test-data
-REPOSITORY_DIR = ../tap-reference-packages-repository
-REPOSITORY_CLOUD_DIR = ${REPOSITORY_DIR}/repository/packages/${CLOUD_NAME}
+#TODO purge older versions according to a TBD retention policy
-SHELL := $(shell which bash)
+kctrl-release-prepare:
+ ytt --data-values-file ${PACKAGE_DIR}/package-metadata.yml -f config/carvel/package-resources -f ${PACKAGE_DIR}/package-metadata.yml -v version=${PACKAGE_VERSION} > ${PACKAGE_DIR}/package-resources.yml
+ ytt --data-values-file ${PACKAGE_DIR}/package-metadata.yml -f config/carvel/package-build -v registry=${PACKAGE_REGISTRY} -v repository=${PACKAGE_REPOSITORY} > ${PACKAGE_DIR}/package-build.yml
-KDEV_SA_NAME=development
-KDEV_SA_NAMESPACE=default
+kctrl-release: kctrl-release-prepare
+ kctrl package release --chdir "${PACKAGE_DIR}" --version="${PACKAGE_VERSION}" --tag="${PACKAGE_VERSION}" --repo-output="${PWD}/${CARVEL_REPO_DIR}" -y
-DEV_PACKAGE_NAME ?= ${PACKAGE_NAME}
-DEV_NAMESPACE ?= default
+kctrl-repo-release-prepare:
+ mkdir -p ${CARVEL_REPO_DIR}
+ ytt -f config/carvel/pkgrepo-build -v name=${CARVEL_REPO_NAME} -v registry=${CARVEL_REPO_REGISTRY} -v repository=${CARVEL_REPO_REPOSITORY} > ${CARVEL_REPO_DIR}/pkgrepo-build.yml
-# validate-input:
-# @# echo "PACKAGE_NAME is ${PACKAGE_NAME}"
-# @# echo "PACKAGES_DIR is ${PACKAGES_DIR}"
-# @# echo ""${PACKAGES_DIR}/${PACKAGE_NAME}" is directory:" $(shell [ -d "${PACKAGES_DIR}/${PACKAGE_NAME}" ] && echo "true" || echo "false")
-# @[ -z "${PACKAGE_NAME}" ] && (echo "PACKAGE_NAME is empty" && exit 1)
-# @[ -d "${PACKAGES_DIR}/${PACKAGE_NAME}" ] || $(shell echo "${PACKAGES_DIR}/${PACKAGE_NAME} directory does not exist" && exit 2)
+kctrl-repo-release: kctrl-repo-release-prepare
+ kctrl package repository release --chdir ${CARVEL_REPO_DIR} -v ${CARVEL_REPO_VERSION} -y
-clean:
- rm -f ${PACKAGE_DIR}/package-kdev.yml || true
- rm -f ${PACKAGE_DIR}/package-build.yml || true
- rm -f ${PACKAGE_DIR}/package-resources.yml || true
- rm -rf ${PACKAGE_BUILD_DIR} || true
- rm -rf ${PACKAGE_DIR}/carvel-artifacts/ || true
- rm -rf ${PACKAGE_DIR}/bundle-* || true
- rm -rf ${PACKAGES_REPO_STAGING_DIR} || true
- mkdir -p ${PACKAGES_REPO_STAGING_DIR}
-
-dev: clean
- kubectl create namespace ${DEV_PACKAGE_NAME} || true
- ytt -f ${PACKAGE_DIR}/config -f ${PACKAGE_DIR}/test-data | kbld -f - | kapp deploy -a ${DEV_PACKAGE_NAME} -n ${DEV_NAMESPACE} --debug -y -f -
-
-dev-cleanup:
- kapp delete -a ${DEV_PACKAGE_NAME} -n ${DEV_NAMESPACE} -y
-
-kdev-prepare: clean
- yq e '... comments="" | .spec.template.spec.template[] |= select(.ytt != null).ytt.paths += "test-data"' ${PACKAGE_DIR}/package.yml > ${PACKAGE_DIR}/package-kdev.yml
- cd ${PACKAGE_DIR} && ytt -f package-kdev.yml -f package-metadata.yml -f package-install.yml > package-resources.yml
-
-kdev: kdev-prepare
- cd ${PACKAGE_DIR} && kctrl dev -f package-resources.yml -l
-
-kdev-cleanup: kdev-prepare
- cd ${PACKAGE_DIR} && kctrl dev -f package-resources.yml -l --delete
-
-imgpkg-push-prepare:
- mkdir -p ${PACKAGE_BUILD_DIR}/.imgpkg
- cp -a ${PACKAGE_DIR}/config ${PACKAGE_BUILD_DIR}
- kbld -f ${PACKAGE_BUILD_DIR}/config/ --imgpkg-lock-output ${PACKAGE_BUILD_DIR}/.imgpkg/images.yml
-
-imgpkg-push: imgpkg-push-prepare
- imgpkg push -b ${PACKAGE_REGISTRY}/${PACKAGE_REPOSITORY}:${PACKAGE_VERSION} -f ${PACKAGE_BUILD_DIR}/
-
-release-prepare:
- cd ${PACKAGE_DIR} && ytt -f package.yml -f package-metadata.yml -f package-install.yml > package-resources.yml
- cd ${PACKAGE_DIR} && ytt -f package-build.ytt.yml -f package-build-schema.yml \
- -v package_fqdn=$(shell yq e .metadata.name ${PACKAGE_DIR}/package-metadata.yml) \
- -v repository=${PACKAGE_REPOSITORY} -v registry=${PACKAGE_REGISTRY} > package-build.yml
- mkdir -p ${PACKAGE_DIR}/.imgpkg
- kbld -f ${PACKAGE_DIR}/config/ --imgpkg-lock-output ${PACKAGE_DIR}/.imgpkg/images.yml
-
-# Does not use the version, and thus cannot be used until kctrl fixes that
-kctrl-release: release-prepare
- kctrl package release --chdir "${PACKAGE_DIR}" --version="${PACKAGE_VERSION}" --repo-output="${PWD}/${PACKAGES_REPO_STAGING_DIR}" -y
-
-create-kdev-sa:
- kubectl create serviceaccount ${KDEV_SA_NAME} -n ${KDEV_SA_NAMESPACE} || true
- kubectl delete clusterrolebinding kdev-cluster-admin || true
- kubectl create clusterrolebinding kdev-cluster-admin --clusterrole=cluster-admin --serviceaccount=${KDEV_SA_NAMESPACE}:${KDEV_SA_NAME}
-
-repo-prepare:
- mkdir -p ${REPOSITORY_CLOUD_DIR}
-
-repo-package-copy: repo-prepare
- cp -a ${PACKAGES_REPO_STAGING_DIR}/packages/$(shell yq e .metadata.name ${PACKAGE_DIR}/package-metadata.yml)/ ${REPOSITORY_CLOUD_DIR}/${PACKAGE_NAME}
+# yq -i '.spec.fetch.imgpkgBundle.image=(.spec.fetch.imgpkgBundle.image|split("@")|.[0])+":"+.metadata.annotations."kctrl.carvel.dev/repository-version"' ${REPO_BASEDIR}/package-repository.yml
crossplane-ytt:
- mkdir -p ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/src/
- ytt -f ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/ytt/crossplane.ytt.yml \
- -f ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/ytt/schema.ytt.yml > ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/src/crossplane.yml
- ytt -f ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/ytt/definition.ytt.yml \
- -f ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/ytt/schema.ytt.yml > ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/src/definition.yml
- ytt -f ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/ytt/composition.ytt.yml \
- -f ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/ytt/schema.ytt.yml > ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/src/composition.yml
+ rm -rf ${PACKAGE_DIR}/.src || true
+ mkdir -p ${PACKAGE_DIR}/.src
+
+ ytt -f ${PACKAGE_DIR}/ytt > ${PACKAGE_DIR}/.src/crossplane.yml
crossplane-build: crossplane-ytt
- rm ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/package/*.xpkg || true
- mkdir -p ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/package
+ rm -rf ${PACKAGE_DIR}/.package || true
+ mkdir -p ${PACKAGE_DIR}/.package
+
up xpkg build \
- --package-root ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/src \
- --examples-root ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/claim-examples \
- --output ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/package/crossplane-${PACKAGE_NAME}.xpkg
+ --package-root ${PACKAGE_DIR}/.src \
+ --examples-root ${PACKAGE_DIR}/claim-examples \
+ --output ${PACKAGE_DIR}/.package/crossplane.xpkg
-crossplane-push:
- up xpkg push --package ${PACKAGE_PROVIDER}/crossplane/${PACKAGE_NAME}/package/crossplane-${PACKAGE_NAME}.xpkg \
+crossplane-push: crossplane-build
+ up xpkg push --package ${PACKAGE_DIR}/.package/crossplane.xpkg \
${PACKAGE_REGISTRY}/${PACKAGE_REPOSITORY}:${PACKAGE_VERSION} \
- --domain ${PACKAGE_REGISTRY}
\ No newline at end of file
diff --git a/config/README.md b/config/README.md
new file mode 100644
index 0000000..f6e1b3b
--- /dev/null
+++ b/config/README.md
@@ -0,0 +1,3 @@
+# Configuration
+
+This directory contains files used for configuring either [the Carvel suite](./carvel) or [Crossplane](./crossplane) to build packages.
diff --git a/config/carvel/README.md b/config/carvel/README.md
new file mode 100644
index 0000000..e69de29
diff --git a/config/carvel/package-build/package-build.ytt.yml b/config/carvel/package-build/package-build.ytt.yml
new file mode 100644
index 0000000..7251f79
--- /dev/null
+++ b/config/carvel/package-build/package-build.ytt.yml
@@ -0,0 +1,39 @@
+#@ load("@ytt:data", "data")
+#@ load("@ytt:assert", "assert")
+
+#@ if data.values.registry == "":
+#@ assert.fail('data value "registry" must not be empty')
+#@ end
+#@ if data.values.repository == "":
+#@ assert.fail('data value "repository" must not be empty')
+#@ end
+#@ if data.values.metadata.name == "":
+#@ assert.fail('.metadata.name must not be empty')
+#@ end
+
+---
+apiVersion: kctrl.carvel.dev/v1alpha1
+kind: PackageBuild
+metadata:
+ creationTimestamp: null
+ name: #@ data.values.metadata.name
+spec:
+ release:
+ - resource: {}
+ template:
+ spec:
+ app:
+ spec:
+ deploy:
+ - kapp: {}
+ template:
+ - ytt:
+ paths:
+ - config
+ - kbld: {}
+ export:
+ - imgpkgBundle:
+ image: #@ data.values.registry + "/" + data.values.repository
+ useKbldImagesLock: true
+ includePaths:
+ - config
diff --git a/config/carvel/package-resources/package-resources.yml b/config/carvel/package-resources/package-resources.yml
new file mode 100644
index 0000000..e56224c
--- /dev/null
+++ b/config/carvel/package-resources/package-resources.yml
@@ -0,0 +1,6 @@
+---
+apiVersion: packaging.carvel.dev/v1alpha1
+kind: PackageInstall
+---
+apiVersion: data.packaging.carvel.dev/v1alpha1
+kind: Package
diff --git a/config/carvel/pkgrepo-build/pkgrepo-build.ytt.yml b/config/carvel/pkgrepo-build/pkgrepo-build.ytt.yml
new file mode 100644
index 0000000..c8c14c4
--- /dev/null
+++ b/config/carvel/pkgrepo-build/pkgrepo-build.ytt.yml
@@ -0,0 +1,10 @@
+#@ load("@ytt:data", "data")
+---
+apiVersion: kctrl.carvel.dev/v1alpha1
+kind: PackageRepositoryBuild
+metadata:
+ name: #@ data.values.name
+spec:
+ export:
+ imgpkgBundle:
+ image: #@ data.values.registry + "/" + data.values.repository
diff --git a/docs/ci/carvel-crossplane-packages.md b/docs/ci/carvel-crossplane-packages.md
new file mode 100644
index 0000000..2747a60
--- /dev/null
+++ b/docs/ci/carvel-crossplane-packages.md
@@ -0,0 +1,126 @@
+---
+title: Carvel and Crossplane packages
+---
+
+We store the code and configuration for the reference packages in the `packages` folder,
+in a structure like the following:
+
+```text
+packages
+├──
+│ └──
+│ └──
+│ ├── ...
+│ ├── ...
+┆ ┆
+```
+
+- `PROVIDER`: the provider we build the package for (i.e., `aws`, `azure`, `gcp`, `multicloud`).
+- `PACKAGING`: the packaging system used (currently, it must be either `carvel` or `crossplane`).
+- `NAME`: the package name.
+
+The following diagram shows that multiple packages can be built and tested in parallel via the `publish-packages.yml` workflow.
+
+```mermaid
+graph LR
+ A(List packages) --> B(Bump version)
+ B --> C
+ B --> D
+
+ subgraph Crossplane
+ D(Publish Crossplane packages) --> G(Test Crossplane packages)
+ end
+
+ subgraph Carvel
+ C(Publish Carvel packages) --> E(Publish Carvel repository)
+ E --> F(Test Carvel packages)
+ end
+
+ click A "#list-packages"
+ click B "#bump-version"
+ click C "#publish-carvel-packages"
+ click E "#publish-carvel-repository"
+ click F "#test-carvel-packages"
+ click D "#publish-crossplane-packages"
+ click G "#test-crossplane-packages"
+```
+
+This workflow runs upon a push event to either `main` or `develop` branches and pull request events targeting the `main` branch.
+Only the files matching the `packages/*/*/*/**` glob pattern trigger the workflow: this reflects the packages structure described before
+and prevents changes to files outside actual package directories from triggering it.
+
+## List packages
+
+This job evaluates the differences since the previous commit and builds two lists of packages, for Carvel and Crossplane, to feed the following GitHub Actions matrix jobs.
+
+## Bump version
+
+The repository is tagged with a new version, bumping the latest one by either the major, minor, or patch fields.
+The git comment determines which section of the version we increment. Depending on the occurrence of `#major,` `#minor`, `#patch` (default is `patch`).
+You can find more information [here](https://github.com/anothrNick/github-tag-action/tree/1.57.0).
+
+## Publish Carvel packages
+
+We build the changed Carvel packages in parallel and publish them to GitHub Packages using [Carvel's `kctrl`](https://carvel.dev/kapp-controller/docs/v0.43.2/kctrl-package-authoring/) CLI.
+`kctrl` also produces package metadata files needed to build the Carvel repository. We store these metadata files in a separate (temporary) git branch for each package.
+
+## Publish Carvel repository
+
+We collect the packages' metadata files produced in the previous job into one branch. Finally, we use `kctrl` to author the pre-release Carvel repository and publish it to GitHub Packages in OCI format.
+The artifact produced by `kctrl` is a manifest file for the `PackageRepository` resource, useful for quickly deploying the repository on top of a kapp-enabled Kubernetes cluster.
+This file is attached to a pre-release; we use this file in the following job for testing the packages.
+
+!!! Note "Carvel Repository"
+ Because we build this Carvel repository to test the packages, we create a pre-release.
+ The final repository release is taken care of by a different workflow.
+
+## Test Carvel packages
+
+This phase aims at testing the Carvel packages by spawning one (GitHub Workflow) job per package and running them in parallel on separate GitHub Actions runners.
+Each job performs the following steps:
+
+1. [install the Carvel suite](https://carvel.dev/#install)
+1. create a [kind cluster](https://kind.sigs.k8s.io/)
+1. [install kapp-controller](https://carvel.dev/kapp-controller/docs/v0.43.2/install/)
+1. [install secretgen-controller](https://github.com/carvel-dev/secretgen-controller/blob/develop/docs/install.md)
+1. install the Carvel repository published in the previous job
+1. run a test script that adheres to a specific naming convention (`scripts/carvel-e2e-$PACKAGE_PROVIDER-$PACKAGE_NAME.sh`), to:
+
+ 1. install the cloud operator specific to the current package (i.e., [ACK](https://aws-controllers-k8s.github.io/community/docs/community/overview/), [ASO](https://azure.github.io/azure-service-operator/), ...)
+ 1. deploy the package with sample input data (need to consider also firewall rules to allow network traffic)
+ 1. deploy an application to consume the service provided via the package
+ 1. ingest data into the application
+ 1. assert the ingested data
+
+1. clean up everything
+
+## Publish Crossplane packages
+
+We build Crossplane packages leveraging `ytt`, for dealing with templating, and the `up` CLI from Upbound, for building the packages and pushing them to an OCI registry (GitHub Packages).
+
+## Test Crossplane packages
+
+The test phase is similar to Carvel's, which means:
+
+1. create a [kind cluster](https://kind.sigs.k8s.io/)
+1. install [Upbound's Universal Crossplane](https://github.com/upbound/universal-crossplane)
+1. run a test script that adheres to a specific naming convention (`scripts/crossplane-e2e-$PACKAGE_PROVIDER-$PACKAGE_NAME.sh`), to:
+
+ 1. install the Crossplane provider(s) needed by the package
+ 1. install the Crossplane package
+ 1. create a claim to trigger the build of the final managed resources
+ 1. deploy and test an application
+
+1. clean up everything
+
+!!! warning
+ Testing Crossplane packages in standard GitHub Actions runners might be tricky:
+ In fact, standard runners have two vCPUs and 7 GB of RAM at the time of writing (specs [here](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources)).
+ This is on the low side for [serving the number of APIs](https://blog.upbound.io/scaling-kubernetes-to-thousands-of-crds/) that Crossplane and its providers can bring (especially the AWS one).
+ Unfortunately, this leads to race conditions that might make the workflows either fail or succeed at unpredictable stages.
+
+ However, it is possible to use the very same scripts provided and test the packages on a larger Kubernetes cluster
+ or make use of **paid** [GitHub-hosted large runners](https://docs.github.com/en/actions/using-github-hosted-runners/using-larger-runners).
+ Although standard Linux runners are free for public repositories, larger ones are not.
+ However, you can look at the [per-minutes rates page](https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions#per-minute-rates),
+ to get an overview of the price of the various instances.
diff --git a/docs/ci/documentation.md b/docs/ci/documentation.md
new file mode 100644
index 0000000..dcbde0a
--- /dev/null
+++ b/docs/ci/documentation.md
@@ -0,0 +1,35 @@
+---
+title: Documentation
+---
+
+The current documentation is written using Markdown syntax and translated into static web pages
+with [`mkdocs`](https://www.mkdocs.org/), which are then published to [GitHub Pages](https://pages.github.com/) and directly linked to the source code repository.
+
+Via a single `mkdocs.yml` file, it is possible to fine-tune `mkdocs`, its extensions, and the theme of choice ([Material](https://squidfunk.github.io/mkdocs-material/)), thus creating very nice-looking and tidy pages.
+The structure of the resulting website is defined in that file, too.
+
+Depending on the specific git event, different workflows may be triggered
+when docs files get added or updated inside the `docs` folder or the `mkdocs.yml` file is changed.
+
+Pull requests to the `main` branch trigger the `build-docs.yml` workflow that does the build to validate the syntax.
+In the following diagram, we create a pull request at commit `D`, and the following commits on the same branch (`E` and `F`) cause pull request synchronize events.
+All commits `D`, `E`, and `F` trigger the `build-docs.yml` workflow.
+
+When we accept the pull request, the code gets merged to the main branch (commit `G`),
+and the `publish-docs.yml` workflow builds and deploys the static website to GitHub Pages.
+
+```mermaid
+%%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': { 'showBranches': true,'showCommitLabel': true, 'rotateCommitLabel': false } } }%%
+gitGraph
+ commit id: "A"
+ commit id: "B"
+ branch docs
+ checkout docs
+ commit id: "C"
+ commit id: "D" type: HIGHLIGHT
+ commit id: "E" type: HIGHLIGHT
+ commit id: "F" type: HIGHLIGHT
+ checkout main
+ merge docs id: "G"
+ commit id: "H"
+```
diff --git a/docs/ci/index.md b/docs/ci/index.md
new file mode 100644
index 0000000..3a3e429
--- /dev/null
+++ b/docs/ci/index.md
@@ -0,0 +1,31 @@
+---
+title: Continuous Integration
+---
+
+Continuous integration practices help reduce time-consuming activities as well as human errors,
+by defining some standards and automating operations, such as builds and tests.
+
+To maintain a clean and working main branch that can be a trusted source of code and documentation, we make changes exclusively via a pull request(PR).
+
+The PR-related events trigger workflows to run repetitive tasks that are automated, such as tests,
+thus reducing the toil on code maintainers.
+
+This repository features a few GitHub Actions workflows to handle different components, such as:
+
+- [Documentation](./documentation.md)
+- [Carvel and Crossplane packages](./carvel-crossplane-packages.md)
+- Carvel repository
+
+!!! note
+ [GitHub Actions reusable workflows](https://docs.github.com/en/actions/using-workflows/reusing-workflows) are being used in order to simplify
+ writing and managing pipelines that become more readable and easier to maintain, as well as making it possible to reuse them effectively
+ in other pipelines to respond to different events.
+
+ They allow writing specific workflows, like functions in programming languages. These workflows can read inputs and produce outputs
+ that they pass along to other workflows.
+ It's important to highlight two things that might not be trivial:
+
+ 1. A workflow does not automatically transfer Environment variables to its children.
+ To pass values from one to the others, you use the inputs/outputs.
+ 1. Secrets are not immediately visible in children's workflows:
+ They must either be defined one by one or declare explicit inheritance. More info [here](https://docs.github.com/en/actions/using-workflows/reusing-workflows#passing-secrets-to-nested-workflows).
diff --git a/docs/crossplane/providers/aws.md b/docs/crossplane/providers/aws.md
index b1958eb..b29ab11 100644
--- a/docs/crossplane/providers/aws.md
+++ b/docs/crossplane/providers/aws.md
@@ -23,7 +23,7 @@ and store the desired release into the `PROVIDER_AWS_RELEASE` variable.
=== "Upbound CLI"
Do make sure you have installed the `up` CLI as described [here](../index.md) and execute
```sh
- up controlplane provider install xpkg.upbound.io/upbound/provider-aws:${PROVIDER_AWS_RELEASE} --name provider-aws
+ up controlplane provider install xpkg.upbound.io/upbound/provider-aws:${PROVIDER_AWS_RELEASE} --name upbound-provider-aws
```
=== "YAML manifest"
@@ -32,7 +32,7 @@ and store the desired release into the `PROVIDER_AWS_RELEASE` variable.
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
- name: provider-aws
+ name: upbound-provider-aws
spec:
package: xpkg.upbound.io/upbound/provider-aws:${PROVIDER_AWS_RELEASE}
EOF
@@ -87,7 +87,7 @@ cat > ${ROLE_TRUST_POLICY} <=v0.12.0"
+
+ crossplane:
+ #@schema/title "CrossplaneNamespace"
+ #@schema/desc "The namespace where crossplane controller is installed"
+ namespace: upbound-system
+ version: '^v1.10'
+
+ #@schema/title "StoreConfig"
+ #@schema/desc "Details of the StoreConfig"
+ storeConfig:
+ #@schema/title "StoreConfig Name"
+ #@schema/desc "The name of the StoreConfig"
+ name: "default"
+ ```
+
+### Solving For Re-usable Snippets
+
+In the templating section, you can see how we reuse values from the schema to avoid duplication and misconfiguration.
+
+Even so, the number of shared data structures between the **Compositions** is significant.
+The solution for the **ConnetionDetails**, for example, is something each **Composition** requires.
+
+So there is a need for even more reuse.
+Not just the data values, but entire data structures, for example, the Composition ***Resources***.
+
+For that purpose, we use the **YTT** features [Load](https://carvel.dev/ytt/docs/v0.44.0/lang-ref-load/) and [Functions](https://carvel.dev/ytt/docs/v0.44.0/lang-ref-def/).
+It lets you create functions in separate files you import into other YTT files.
+
+!!! Info "YTT Library"
+ We could also have opted to use a **YTT** [Library](https://carvel.dev/ytt/docs/v0.44.0/lang-ref-ytt-library/).
+
+ Which does practically the same thing but requires more steps to use.
+ So we opted for the solution below.
+
+To use this, we do the following:
+
+1. Create a file named `.lib.yml`
+1. Add ***Functions*** in this file that generate the Crossplane Composition Resource snippets
+1. Load the file and the desired functions in the YTT _data_ file (a) file starting with `#@ load("@ytt:data", "data")`)
+1. Use the functions as if they are defined in this YTT data file
+
+Let's look at some examples to clarify what we did.
+First, it's a function in a _library_ module.
+
+!!! Example "shared.lib.yml"
+
+ For clarity, we removed some lines and highlighted the noteworthy lines.
+
+ We **start** a function with `#@ def ()`.
+
+ We **end** a function with `#@ end`.
+
+ We can use any defined input parameters directly by name.
+ We determine the value by the order in which the caller supplies them.
+
+ In this case, we have one parameter, `infra`.
+ This lets us create a dynamic label on the secret based on the type of infrastructure the **Composition** implements (e.g., `azure`, `aws`, `kubernetes`).
+
+ ```yaml hl_lines="1 15 18" title="shared.lib.yml"
+ #@ def labelsForSecret(infra):
+ name: connectionSecret
+ base:
+ apiVersion: kubernetes.crossplane.io/v1alpha1
+ kind: Object
+ spec:
+ forProvider:
+ manifest:
+ apiVersion: v1
+ kind: Secret
+ spec: {}
+ metadata:
+ labels:
+ services.apps.tanzu.vmware.com/class: multicloud-psql
+ services.apps.tanzu.vmware.com/infra: #@ infra
+ patches:
+ ...
+ #@ end
+ ```
+
+We use the library as follows:
+
+1. We import it using the `#@ load()` feature. Using the relative path of the file and the functions we want to use.
+ ```yaml
+ #@ load("shared.lib.yml", "labelsForSecret")
+ ```
+1. Then, we call the function where we want its _output_ to be.
+ ```yaml
+ resources:
+ - #@ labelsForSecret("aws")
+ ```
+
+!!! Important
+ By default, a function returns the data structure that it defines.
+
+ If you want to return a single value, you can do so via the `return` statement like this:
+
+ ```yaml
+ #@ def TLSSecretName(domain):
+ #@ return str(domain).replace(".", "-") + "-tls"
+ #@ end
+ ```
+
+The functions can use everything **YTT** has to offer.
+
+This makes it an excellent place to handle specific logic for **Crossplane Compositions**.
+For example, in the case of the AWS RDS instance, we have private and public variants.
+
+This means that while most values for the RDS instance definition are the same, some change if it needs to be public.
+
+!!! Example "AWS Composition Public/Private Switch"
+
+ When we make the RDS instance publicly available, we need to set `spec.forProvider.publiclyAccessible` to true.
+
+ We also need to tell **Crossplan** which ***SubnetGroup*** it has to use.
+ When we make the RDS instance public, we create the ***SubnetGroup*** ourselves and let **Crossplane** manage the reference:
+
+ ```yaml
+ #@ if/end publiclyAccessible:
+ dbSubnetGroupNameSelector:
+ matchControllerRef: true
+ ```
+ _ps. `#@ if/end` means it does an if/else/end expression for a single line only_
+
+ If private, we expect you to supply the name of an existing one.
+ We can use the `not` keyword to reverse the `if/end`, so we end up with two variations of the same snippet.
+
+ ```yaml
+ #@ if/end not publiclyAccessible:
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.aws.dbSubnetGroupName
+ toFieldPath: spec.forProvider.dbSubnetGroupName
+ ```
+
+ ```yaml hl_lines="13 19 25" title="aws-composition.lib.yml"
+ #@ def rdsInstance(crossplaneNamespace, providerConfigRef, publiclyAccessible):
+ name: rdsinstance
+ base:
+ apiVersion: rds.aws.upbound.io/v1beta1
+ kind: Instance
+ spec:
+ forProvider:
+ engine: postgres
+ instanceClass: db.t3.micro
+ passwordSecretRef:
+ key: password
+ namespace: #@ crossplaneNamespace
+ publiclyAccessible: #@ publiclyAccessible
+ skipFinalSnapshot: true
+ storageEncrypted: false
+ allocatedStorage: 10
+ vpcSecurityGroupIdSelector:
+ matchControllerRef: true
+ #@ if/end publiclyAccessible:
+ dbSubnetGroupNameSelector:
+ matchControllerRef: true
+ providerConfigRef:
+ name: #@ providerConfigRef
+ patches:
+ #@ if/end not publiclyAccessible:
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.aws.dbSubnetGroupName
+ toFieldPath: spec.forProvider.dbSubnetGroupName
+ ...
+ #@ end
+ ```
+
+Let's look at the two AWS examples (private and public) to see how this works out for the **Composition** files.
+
+#### AWS Private Example
+
+```yaml title="aws-composition-private.ytt.yml"
+#@ load("@ytt:data", "data")
+---
+#@ load("aws-composition.lib.yml", "rdsInstance", "securityGroup", "securityGroupRule")
+#@ load("shared.lib.yml", "labelsForSecret", "tfProviderConfig", "tfWorkspace")
+
+apiVersion: apiextensions.crossplane.io/v1
+kind: Composition
+metadata:
+ name: #@ data.values.providers.aws.name + "-" + data.values.cloudServiceBindingType + "-private"
+ labels:
+ crossplane.io/xrd: #@ data.values.xrd.names.plural + "." + data.values.xrd.group
+ provider: #@ data.values.providers.aws.name
+ database: #@ data.values.cloudServiceBindingType
+ connectivity: "private"
+spec:
+ writeConnectionSecretsToNamespace: #@ data.values.crossplane.namespace
+ compositeTypeRef:
+ apiVersion: #@ data.values.xrd.group + "/" + data.values.xrd.version
+ kind: #@ data.values.xrd.names.kind
+ resources:
+ - #@ labelsForSecret("aws")
+ - #@ tfProviderConfig(data.values.crossplane.namespace)
+ - #@ tfWorkspace(data.values.crossplane.namespace)
+ - #@ rdsInstance(data.values.crossplane.namespace, data.values.providers.aws.configRef, False)
+ - #@ securityGroup()
+ - #@ securityGroupRule()
+```
+
+#### AWS Public Example
+
+!!! Info "Convenience Variables"
+
+ YTT supports the use of convenience variables.
+
+ Below you can see we make ample use of them to make the subnet parameters clearer to understand and separate.
+
+```yaml title="aws-composition-public.ytt.yml"
+#@ load("@ytt:data", "data")
+---
+#@ load("aws-composition.lib.yml", "rdsInstance", "securityGroup", "securityGroupRule", "subnet", "routeTableAssociation", "routeTable", "route", "subnetGroup")
+#@ load("shared.lib.yml", "labelsForSecret", "tfProviderConfig", "tfWorkspace")
+
+#@ subnetASuffix = '-a'
+#@ subnetAFormat = 'subnet-a-%s'
+#@ availabilityZoneAFormat = '%sa'
+#@ cidrBlockFieldA = 'spec.parameters.aws.public.subnetACidrBlock'
+
+#@ subnetBSuffix = '-b'
+#@ subnetBFormat = 'subnet-b-%s'
+#@ availabilityZoneBFormat = '%sb'
+#@ cidrBlockFieldB = 'spec.parameters.aws.public.subnetBCidrBlock'
+
+apiVersion: apiextensions.crossplane.io/v1
+kind: Composition
+metadata:
+ name: #@ data.values.providers.aws.name + "-" + data.values.cloudServiceBindingType + "-public"
+ labels:
+ crossplane.io/xrd: #@ data.values.xrd.names.plural + "." + data.values.xrd.group
+ provider: #@ data.values.providers.aws.name
+ database: #@ data.values.cloudServiceBindingType
+ connectivity: "public"
+spec:
+ writeConnectionSecretsToNamespace: #@ data.values.crossplane.namespace
+ compositeTypeRef:
+ apiVersion: #@ data.values.xrd.group + "/" + data.values.xrd.version
+ kind: #@ data.values.xrd.names.kind
+ resources:
+ - #@ labelsForSecret("aws")
+ - #@ tfProviderConfig(data.values.crossplane.namespace)
+ - #@ tfWorkspace(data.values.crossplane.namespace)
+ - #@ rdsInstance(data.values.crossplane.namespace, data.values.providers.aws.configRef, True)
+ - #@ securityGroup()
+ - #@ securityGroupRule()
+ - #@ routeTable()
+ - #@ subnetGroup()
+ - #@ subnet(subnetASuffix, subnetAFormat, availabilityZoneAFormat, cidrBlockFieldA)
+ - #@ routeTableAssociation(subnetASuffix, subnetAFormat)
+ - #@ subnet(subnetBSuffix, subnetBFormat, availabilityZoneBFormat, cidrBlockFieldB)
+ - #@ routeTableAssociation(subnetBSuffix, subnetBFormat)
+ - #@ route('route-%s')
+```
diff --git a/docs/usecases/multicloud/packages/psql/index.md b/docs/usecases/multicloud/packages/psql/index.md
new file mode 100644
index 0000000..deafafa
--- /dev/null
+++ b/docs/usecases/multicloud/packages/psql/index.md
@@ -0,0 +1,969 @@
+---
+title: Consuming Postgresql Multicloud
+---
+
+## Why Multicloud
+
+There are various reasons why companies are running applications in multiple clouds.
+We count data centers as local or private clouds.
+
+It is increasingly common for companies to run Kubernetes clusters in multiple locations - some clusters in the data center and others in AWS, for example.
+
+When (service) consumers do not care about the specific performance parameters of a database, you can simplify onboarding and relocation pain by providing a generic API.
+
+This package is an example of such a generic API.
+It holds as long as the consumers want _a database_ and there is no need (yet) to fine-tune it.
+
+Good examples are PoCs, preview environments, or applications where the performance bottleneck lies elsewhere.
+
+## Pre-requisites
+
+This package supports three platforms; Kubernetes, Azure, and AWS.
+Therefore, we split the prerequisites into shared requirements and cloud-specific (Azure and AWS, respectively).
+
+### Shared
+
+!!! Warning "Terraform provider configuration included"
+ The package creates a **ProviderConfig** for the Terraform provider for you. To ensure it is unique, it has the UID from Composite Resource (`XPostgreSQLInstance`) as the name.
+
+ So when installing the **Terraform Provider** (to meet the prerequisites), there is no need to create a **ProviderConfig**.
+
+* Kubernetes 1.23+
+* [Crossplane 1.10+](/tanzu-application-platform-reference-service-packages/crossplane/)
+* [Crossplane Kubernetes provider (Community)](/tanzu-application-platform-reference-service-packages/crossplane/providers/kubernetes/)
+* [Crossplane Terraform provider (Upbound official)](/tanzu-application-platform-reference-service-packages/crossplane/providers/terraform/)
+
+### Kubernetes
+
+* [Crossplane Helm provider (Community)](/tanzu-application-platform-reference-service-packages/crossplane/providers/helm/)
+
+### Azure
+
+When wanting to consume the Azure FlexibleServer variant, you also need the following:
+
+* Azure credentials
+* [Crossplane Azure provider (Upbound official)](/tanzu-application-platform-reference-service-packages/crossplane/providers/azure/)
+
+### AWS
+
+* AWS credentials
+* [Crossplane AWS provider (Upbound official)](/tanzu-application-platform-reference-service-packages/crossplane/providers/aws/)
+
+## Install Crossplane Package
+
+We start by setting some environment variables and installing our Crossplane package (Configuration CR).
+
+```sh
+CONFIG_NAME="trp-multicloud-psql"
+CONFIG_IMAGE="ghcr.io/vmware-tanzu-labs/trp-multicloud-psql"
+CONFIG_VERSION="0.1.0-rc-4"
+CROSSPLANE_NAMESPACE="upbound-system"
+```
+
+```sh
+cat < resource from Crossplane).
+
+ ```sh
+ kubectl describe xpostgresqlinstances.multi.ref.services.apps.tanzu.vmware.com/${CLAIM_NAME}
+ ```
+
+!!! Failure "Cannot render composed resource"
+ If you get a warning like this in the **Composite Resource**:
+
+ ```sh
+ cannot render composed resource from resource template at index 1: cannot use dry-run create to name composed resource: workspaces.tf.crossplane.io is forbidden: User "system:serviceaccount:upbound-system:crossplane" cannot create resource "workspaces" in API group "tf.crossplane.io" at the cluster scope
+ ```
+
+ Sometimes the service account of Crossplane or a Crossplane Provider does not have enough permissions to create the resources.
+ You can either fix this quickly and dirty (for PoCs) like below:
+
+ ```sh
+ kubectl create clusterrolebinding crossplane-admin-binding --clusterrole cluster-admin --serviceaccount="upbound-system:crossplane" || true
+ ```
+
+ Or find the Service Account for the specific provider (they have generated names) and update its RBAC configuration concisely.
+
+ ```sh
+ SA=$(kubectl -n upbound-system get sa -o name | grep provider-terraform | sed -e 's|serviceaccount\/|upbound-system:|g')
+ ```
+
+!!! Success
+ If the wait commands returns with `Conditions met`, the package is ready.
+
+ You can now continue with [test without TAP](#test-withouth-tap) or [test with TAP and STK](#test-with-tap-stk).
+
+## Test Without TAP
+
+Tanzu Application Platform (TAP) and the Services Toolkit (STK) are great tools for usage at scale.
+It can be rather cumbersome to test a single service package.
+
+So below is an example of a Kubernetes deployment that directly consumes the secret created by Crossplane.
+It uses the [Service Bindings](https://servicebinding.io/) specification (Spring library) to translate the secret to the parameters needed by the database API.
+
+### Install the Test application
+
+```sh hl_lines="21 22 23 39 40 41 42 49"
+cat < workload.yml
+ ```
+
+ ```sh
+ kubectl apply -f workload.yml
+ ```
+
+To see the logs:
+```sh
+tanzu apps workload tail spring-boot-postgres-01
+```
+
+To get the status:
+
+```sh
+tanzu apps workload get spring-boot-postgres-01
+```
+
+Tap creates the deployment when the build and config writer workflows are complete.
+
+To see their pods:
+
+```sh
+kubectl get pod -l app.kubernetes.io/part-of=spring-boot-postgres-01
+```
+
+Then you can wait for the application to be ready via the `kubectl` CLI.
+
+```sh
+kubectl wait --for=condition=ready \
+ pod -l app.kubernetes.io/component=run,app.kubernetes.io/part-of=spring-boot-postgres-01 \
+ --timeout=180s \
+ --namespace ${TAP_DEV_NAMESPACE}
+```
+
+Ensure there is only one deployment active:
+
+```sh
+kubectl get pod --namespace ${TAP_DEV_NAMESPACE} \
+ -l app.kubernetes.io/component=run,app.kubernetes.io/part-of=spring-boot-postgres-01
+```
+
+Which should list a single deployment with Ready 2/2:
+
+```sh
+NAME READY STATUS RESTARTS AGE
+spring-boot-postgres-01-00002-deployment-8cd56bdc8-gb44n 2/2 Running 0 6m11s
+```
+
+We then collect the name of the **Pod** or copy it from the command-line output.
+
+```sh
+POD_NAME=$(kubectl get pod \
+ --namespace ${TAP_DEV_NAMESPACE} \
+ -l app.kubernetes.io/component=run,app.kubernetes.io/part-of=spring-boot-postgres-01 \
+ -o jsonpath="{.items[0].metadata.name}")
+```
+
+### Test Application
+
+Use the `kubectl` CLI to create a port forward.
+
+```sh
+kubectl port-forward pod/${POD_NAME} --namespace ${TAP_DEV_NAMESPACE} 8080
+```
+
+And then open another terminal to test the application:
+
+```sh
+curl -s "http://localhost:8080"
+```
+
+Which should return an empty list `[]`.
+Add a value that gets stored in the MongoDB instance.
+
+```sh
+curl --header "Content-Type: application/json" \
+ --request POST --data '{"name":"Alice"}' \
+ http://localhost:8080/create
+```
+
+Making another GET request should return the stored entry:
+
+```sh
+curl -s "http://localhost:8080"
+```
+
+!!! Success
+ We have gone through all the steps. You can now clean up all the resources.
+
+## Cleanup
+
+```sh
+CLAIM_NAME="postgresql-0001"
+CONFIG_NAME="trp-multicloud-psql"
+```
+
+!!! Danger "Delete TAP Workload"
+ ```sh
+ tanzu apps workload delete spring-boot-postgres-01 || true
+ ```
+
+
+!!! Danger "Delete STK Claim"
+ ```sh
+ tanzu service claim delete multi-psql-claim-01 || true
+ kubectl delete ClusterInstanceClass multi-psql multi-psql-kubernetes multi-psql-kubernetes-12 || true
+ ```
+
+
+!!! Danger "Delete Crossplane Claim & Package"
+ ```sh
+ kubectl delete postgresqlinstances ${CLAIM_NAME} || true
+ ```
+
+ ```sh
+ kubectl delete configuration ${CONFIG_NAME} || true
+ ```
+
+!!! Danger "Delete Crossplane resources"
+
+ ```sh
+ kubectl delete providerconfigs.helm.crossplane.io default || true
+ kubectl delete providerconfigs.kubernetes.crossplane.io default || true
+
+ kubectl delete providers.pkg.crossplane.io crossplane-contrib-provider-helm || true
+ kubectl delete providers.pkg.crossplane.io crossplane-contrib-provider-kubernetes || true
+ kubectl delete providers.pkg.crossplane.io crossplane-contrib-provider-terraform || true
+ ```
+
+This should not be required, but in case the resources for Azure are not cleaned up when deleting the package:
+
+!!! Danger "Delete Azure Resources"
+ ```sh
+ kubectl delete flexibleserverconfigurations.dbforpostgresql.azure.upbound.io -l crossplane.io/claim-name=${CLAIM_NAME} --force --grace-period=0 || true
+ kubectl delete flexibleserverdatabases.dbforpostgresql.azure.upbound.io -l crossplane.io/claim-name=${CLAIM_NAME} --force --grace-period=0 || true
+ kubectl delete flexibleserverfirewallrules.dbforpostgresql.azure.upbound.io -l crossplane.io/claim-name=${CLAIM_NAME} --force --grace-period=0 || true
+
+ FLEXIBLE_SERVER_NAME=$(kubectl get flexibleserver.dbforpostgresql.azure.upbound.io -l crossplane.io/claim-name=${CLAIM_NAME} -o name)
+ kubectl patch ${FLEXIBLE_SERVER_NAME} -p '{"metadata":{"finalizers":null}}' --type=merge || true
+ kubectl delete flexibleserver.dbforpostgresql.azure.upbound.io -l crossplane.io/claim-name=${CLAIM_NAME} --force --grace-period=0 || true
+ ```
\ No newline at end of file
diff --git a/mkdocs.yml b/mkdocs.yml
index c184340..3fedf12 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -87,7 +87,7 @@ markdown_extensions:
custom_fences:
- name: mermaid
class: mermaid
- format: !!python/name:pymdownx.superfences.fence_code_format
+ format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.tabbed:
alternate_style: true
- pymdownx.tasklist:
@@ -115,8 +115,20 @@ nav:
- usecases/azure/packages/mongodb/crossplane/consume-package.md
- Prerequisites:
- usecases/azure/prerequisites/index.md
+ - Multicloud:
+ - Packages:
+ - Postgresql with Crossplane:
+ - usecases/multicloud/packages/psql/index.md
+ - usecases/multicloud/packages/psql/create.md
- Crossplane:
- crossplane/index.md
- Providers:
- crossplane/providers/aws.md
- crossplane/providers/azure.md
+ - crossplane/providers/terraform.md
+ - crossplane/providers/helm.md
+ - crossplane/providers/kubernetes.md
+- CI:
+ - ci/index.md
+ - ci/documentation.md
+ - ci/carvel-crossplane-packages.md
\ No newline at end of file
diff --git a/packagerepo.yaml b/packagerepo.yaml
deleted file mode 100644
index 1d0cd8c..0000000
--- a/packagerepo.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
----
-apiVersion: packaging.carvel.dev/v1alpha1
-kind: PackageRepository
-metadata:
- name: tap-reference-service-packages
- namespace: tanzu-package-repo-global #! NOTE this may be different if kapp was not installed through cluster-essentials
- #! the kapp-controller global namespace can be obtained as follows
- #! kubectl -n kapp-controller get deployment kapp-controller -o json | jq -r '.spec.template.spec.containers[]|select(.name=="kapp-controller").args[]|select(.|startswith("-packaging-global-namespace"))|split("=")[1]'
-spec:
- fetch:
- imgpkgBundle:
- image: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages:0.0.4-alpha.0
diff --git a/packages/.gitignore b/packages/.gitignore
new file mode 100644
index 0000000..af5beeb
--- /dev/null
+++ b/packages/.gitignore
@@ -0,0 +1,4 @@
+carvel-artifacts/
+bundle-*/
+package-resources.yml
+package-build.yml
diff --git a/packages/README.md b/packages/README.md
new file mode 100644
index 0000000..8e2e961
--- /dev/null
+++ b/packages/README.md
@@ -0,0 +1,100 @@
+# Packages structure
+
+This is the parent folder holding the configurations the reference packages will be built from.
+In order for the CI/CD workflow to work, they must adhere to the following structure:
+
+```text
+packages
+├──
+│ └──
+│ └──
+│ ├── ...
+│ ├── ...
+┆ ┆
+```
+
+- `PROVIDER`: the provider the package is built for (i.e. `aws`, `azure`, `gcp`, `multicloud`).
+- `PACKAGING`: the packaging system used (currently it must be either `carvel` or `crossplane`).
+- `NAME`: the package name.
+
+All folder names must be lowercase.
+
+An actual example, featuring AWS Elasticache package built with Carvel suite and a multicloud PostgreSQL package with Crossplane, looks like the following:
+
+```text
+packages
+├── aws
+│ └── carvel
+│ └── elasticache
+│ ├── config
+│ │ ├── 00-schema.yml
+│ │ ├── 01-replication-group.ytt.yml
+│ │ └── 99-kapp-config.yml
+│ └── package-metadata.yml
+┆
+└── multicloud
+ └── crossplane
+ └── postgresql
+ ├── README.md
+ ├── claim
+ │ └── helm.yml
+ ├── claim-examples
+ │ └── helm-psql-12.yaml
+ └── ytt
+ ├── crossplane.ytt.yml
+ ├── definition.ytt.yml
+ ├── helm-composition.ytt.yml
+ └── schema.ytt.yml
+```
+
+## Packaging
+
+The contents of the `//` directory depends on the specific packaging system.
+
+### Carvel
+
+The `kctrl` utility from [Carvel suite](https://carvel.dev) is being used to author packages in combination with other tools in the suite (i.e. `ytt`).
+
+The metadata files required to create the package are stored as [ytt templates](../config/carvel/)
+to easily define a standard to create multiple packages.
+
+**N.B. Each package MUST have its own `PackageMetadata` manifest stored in the
+`package-metadata.yml` file, located in the package's main directory.**
+
+This file will be included in the actual package as well as used for holding information
+that is required to fill some fields in other template files, in a DRY fashion.
+
+*DRY: Don't Repeat Yourself
+
+The `config` directory inside the package home holds the ytt files that define the resources
+that will be created by the package installation as well as the schema definition for the input values.
+
+**N.B. The name of such a folder MUST be `config`, as it is hardcoded in the mentioned templates.**
+
+The command used to build the package and publish it to the container registry is `make kctrl-release`,
+which uses a few environment variables for configuration.
+The following snippet defines a quick way of building all the `carvel` packages in the `packages` folder.
+Additionally, it uploads the packages to the ghcr.io service in the `org/repository` repository (do adjust it to your own account),
+in a hierarchy that reflects the filesystem.
+
+```sh
+PACKAGE_REGISTRY="ghcr.io"
+for PACKAGE_DIR in $(find packages -type d -mindepth 3 -maxdepth 3); do
+ if [[ $(cut -d/ -f3 <<<${PACKAGE_DIR}) == "carvel" ]]; then
+ PACKAGE_REPOSITORY="org/repository/${PACKAGE_DIR#packages/}"
+ make kctrl-release
+ fi
+done
+```
+
+You must be authenticated to ghcr.io and you must have the correct permissions to write packages to `org`.
+The authentication credentials can be provided either via [Docker login][docker-login] or via [`imgpkg` environment variables][imgpkg-auth-env], for example:
+
+```sh
+export IMGPKG_REGISTRY_HOSTNAME="ghcr.io"
+export IMGPKG_REGISTRY_USERNAME="my-username"
+export IMGPKG_REGISTRY_PASSWORD="my-personal-access-token"
+```
+
+[imgpkg-auth-env]: https://carvel.dev/imgpkg/docs/v0.34.0/auth/#via-environment-variables
+[docker-login]: https://docs.docker.com/engine/reference/commandline/login/
diff --git a/packages/aws/carvel/elasticache/config/00-schema.yml b/packages/aws/carvel/elasticache/config/00-schema.yml
new file mode 100644
index 0000000..6b37539
--- /dev/null
+++ b/packages/aws/carvel/elasticache/config/00-schema.yml
@@ -0,0 +1,64 @@
+#@data/values-schema
+---
+#@schema/title "Name"
+#@schema/desc "Name of the Elasticache instance."
+name: ""
+
+#@schema/title "Namespace"
+#@schema/desc "Namespace to deploy the kubernetes resources to."
+namespace: ""
+
+#@schema/title "CreateNamespace"
+#@schema/desc "Whether to create the namespace for the resources or not."
+createNamespace: False
+
+#@schema/title "CacheSubnetGroupName"
+#@schema/desc "Name of the cache subnet group to create the replication group in. It must be pre-created if the createCacheSubnetGroup parameter is set to false."
+cacheSubnetGroupName: ""
+
+#@schema/title "CreateCacheSubnetGroup"
+#@schema/desc "Whether to create the CacheSubnetGroup or not."
+createCacheSubnetGroup: false
+
+#@schema/title "SubnetIDs"
+#@schema/desc "The list of subnets to create the CacheSubnetGroup for. Mandatory if the createCacheSubnetGroup parameter is set to true."
+subnetIDs:
+ - ""
+
+#@schema/title "CacheNodeType"
+#@schema/desc "Type of nodes used for the replication group."
+cacheNodeType: "cache.t2.micro"
+
+#@schema/title "VpcSecurityGroupIDs"
+#@schema/desc "The list of security groups to associate to the replication group."
+vpcSecurityGroupIDs:
+ - ""
+
+#@schema/title "InstanceClassName"
+#@schema/desc "The name of the instance class we want to use for binding secrets."
+instanceClassName: aws-elasticache
+#@schema/title "Service Instance Labels"
+#@schema/desc "A set of labels which will be applied to the claimable secret."
+#@schema/default {}
+#@schema/type any=True
+serviceInstanceLabels:
+
+#@schema/title "EngineVersion"
+#@schema/desc "The version of the Redis engine to use. Available versions can be obtained running `aws cdescribe-cache-engine-versions --engine redis`."
+engineVersion: "6.2"
+
+#@schema/title "ReplicasPerNodeGroup"
+#@schema/desc "Number of replicas per node group. Allowed values are from 0 to 5."
+replicasPerNodeGroup: 0
+
+#@schema/title "Tags"
+#@schema/desc "Tags to attach to all the resources."
+#@schema/default []
+tags:
+ -
+ #@schema/title "Key"
+ #@schema/desc "The name of the tag."
+ key: ""
+ #@schema/title "Value"
+ #@schema/desc "The value of the tag."
+ value: ""
diff --git a/packages/aws/carvel/elasticache/config/01-replication-group.ytt.yml b/packages/aws/carvel/elasticache/config/01-replication-group.ytt.yml
new file mode 100644
index 0000000..c393e0c
--- /dev/null
+++ b/packages/aws/carvel/elasticache/config/01-replication-group.ytt.yml
@@ -0,0 +1,221 @@
+#@ load("@ytt:data", "data")
+#@ load("@ytt:assert", "assert")
+#@ load("@ytt:template", "template")
+
+#@ def get_id(x): return "{}-{}".format(data.values.name, x)
+
+#@ def tags(resource_tags=[]):
+#@ return [ x for x in (data.values.tags + resource_tags) if x["key"] != "" and x["value"] != "" ]
+#@ end
+
+#@ users = {
+#@ "reader": { "access_string": "on ~* -@all +@read" },
+#@ "writer": { "access_string": "on ~* +@all" },
+#@ }
+
+#! only Redis is currently supported
+#@ engine = "redis"
+
+#@ replicationGroupName = data.values.name
+
+#@ service_account_name = "{}-{}".format(data.values.name, "elasticache-reader")
+#@ role_name = service_account_name
+
+#! a user with name "default" must be present in every usergroup
+#! the elasticache "default" user has all permissions and no password -> unsuitable
+#! this creates a new "default" user with no permissions
+#@ default_user = { "id": get_id("default"), "access_string": "off ~* -@all" }
+
+#@ mandatory_fields = [ "name", "namespace", "cacheSubnetGroupName", "cacheNodeType", "vpcSecurityGroupIDs" ]
+#@ missing_fields = []
+#@ for field in mandatory_fields:
+#@ if len(data.values[field]) == 0:
+#@ missing_fields.append(field)
+#@ end
+#@ end
+
+#!@ if len(missing_fields) > 0:
+#!@ assert.fail("Missing values for mandatory fields '{}'".format(missing_fields))
+#!@ end
+
+#@ if data.values.createCacheSubnetGroup and len(data.values.subnetIDs) == 0:
+#@ assert.fail("SubnetIDs not provided for CacheSubnetGroup creation")
+#@ end
+
+#@ if data.values.createNamespace and data.values.namespace:
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: #@ data.values.namespace
+#@ end
+---
+apiVersion: elasticache.services.k8s.aws/v1alpha1
+kind: User
+metadata:
+ name: #@ default_user["id"]
+ namespace: #@ data.values.namespace
+spec:
+ accessString: #@ default_user["access_string"]
+ noPasswordRequired: true
+ engine: #@ engine
+ userID: #@ default_user["id"]
+ userName: default
+ tags: #@ tags()
+
+#@ for user_name in users:
+#@ user_id = get_id(user_name)
+#@ user_creds = "{}-{}".format(user_id,"creds")
+#@ users[user_name].update({ "id": user_id, "creds": user_creds })
+---
+apiVersion: secretgen.k14s.io/v1alpha1
+kind: Password
+metadata:
+ name: #@ user_creds
+ namespace: #@ data.values.namespace
+spec:
+ length: 128
+ secretTemplate:
+ type: Opaque
+ stringData:
+ password: $(value)
+---
+apiVersion: secretgen.carvel.dev/v1alpha1
+kind: SecretTemplate
+metadata:
+ name: #@ "{}-bindable".format(user_creds)
+ namespace: #@ data.values.namespace
+spec:
+ serviceAccountName: #@ service_account_name
+ inputResources:
+ - name: replicationGroup
+ ref:
+ apiVersion: elasticache.services.k8s.aws/v1alpha1
+ kind: ReplicationGroup
+ name: #@ data.values.name
+ - name: creds
+ ref:
+ apiVersion: v1
+ kind: Secret
+ name: #@ user_creds
+ template:
+ metadata:
+ labels:
+ services.apps.tanzu.vmware.com/class: #@ data.values.instanceClassName
+ _serviceInstanceLabels: #@ template.replace(data.values.serviceInstanceLabels)
+ type: servicebinding.io/redis
+ stringData:
+ type: redis
+ username: #@ user_name
+ ssl: "true"
+ host: $(.replicationGroup.status.nodeGroups[0].primaryEndpoint.address)
+ port: $(.replicationGroup.status.nodeGroups[0].primaryEndpoint.port)
+ data:
+ password: "$(.creds.data.password)"
+---
+apiVersion: elasticache.services.k8s.aws/v1alpha1
+kind: User
+metadata:
+ name: #@ user_id
+ namespace: #@ data.values.namespace
+spec:
+ accessString: #@ users[user_name]["access_string"]
+ engine: #@ engine
+ passwords:
+ - name: #@ user_creds
+ key: password
+ namespace: #@ data.values.namespace
+ userID: #@ user_id
+ userName: #@ user_name
+ tags: #@ tags()
+#@ end
+---
+apiVersion: elasticache.services.k8s.aws/v1alpha1
+kind: UserGroup
+metadata:
+ name: #@ data.values.name
+ namespace: #@ data.values.namespace
+spec:
+ engine: #@ engine
+ userGroupID: #@ data.values.name
+ userIDs: #@ [ default_user["id"] ] + [ get_id(u) for u in users ]
+ tags: #@ tags()
+
+#@ if data.values.createCacheSubnetGroup:
+---
+apiVersion: elasticache.services.k8s.aws/v1alpha1
+kind: CacheSubnetGroup
+metadata:
+ name: #@ data.values.cacheSubnetGroupName
+ namespace: #@ data.values.namespace
+spec:
+ cacheSubnetGroupDescription: #@ "CacheSubnetGroup for {}".format(data.values.name)
+ cacheSubnetGroupName: #@ data.values.cacheSubnetGroupName
+ subnetIDs: #@ data.values.subnetIDs
+#@ end
+---
+apiVersion: elasticache.services.k8s.aws/v1alpha1
+kind: ReplicationGroup
+metadata:
+ name: #@ replicationGroupName
+ namespace: #@ data.values.namespace
+spec:
+ description: #@ "A {} service instance".format(engine)
+ engine: #@ engine
+ engineVersion: #@ data.values.engineVersion
+ replicationGroupID: #@ replicationGroupName
+ cacheNodeType: #@ data.values.cacheNodeType
+ cacheSubnetGroupName: #@ data.values.cacheSubnetGroupName
+ securityGroupIDs: #@ data.values.vpcSecurityGroupIDs
+ atRestEncryptionEnabled: true
+ transitEncryptionEnabled: true
+ replicasPerNodeGroup: #@ data.values.replicasPerNodeGroup
+ userGroupIDs:
+ - #@ data.values.name
+ tags: #@ tags()
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: #@ service_account_name
+ namespace: #@ data.values.namespace
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ name: #@ role_name
+ namespace: #@ data.values.namespace
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - secrets
+ verbs:
+ - get
+ - list
+ - watch
+ resourceNames: #@ [ users[u]["creds"] for u in users ]
+- apiGroups:
+ - elasticache.services.k8s.aws
+ resources:
+ - replicationgroups
+ verbs:
+ - get
+ - list
+ - watch
+ resourceNames:
+ - #@ data.values.name
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: #@ role_name
+ namespace: #@ data.values.namespace
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: #@ role_name
+subjects:
+- kind: ServiceAccount
+ name: #@ service_account_name
+ namespace: #@ data.values.namespace
diff --git a/packages/aws/carvel/elasticache/config/99-kapp-config.yml b/packages/aws/carvel/elasticache/config/99-kapp-config.yml
new file mode 100644
index 0000000..bf40b13
--- /dev/null
+++ b/packages/aws/carvel/elasticache/config/99-kapp-config.yml
@@ -0,0 +1,43 @@
+---
+apiVersion: kapp.k14s.io/v1alpha1
+kind: Config
+
+waitRules:
+- supportsObservedGeneration: false
+ conditionMatchers:
+ - type: ACK.ResourceSynced
+ status: "True"
+ success: true
+ - type: ACK.Terminal
+ status: "True"
+ failure: true
+ resourceMatchers:
+ - apiVersionKindMatcher: {apiVersion: elasticache.services.k8s.aws/v1alpha1, kind: ReplicationGroup}
+ - apiVersionKindMatcher: {apiVersion: elasticache.services.k8s.aws/v1alpha1, kind: User}
+ - apiVersionKindMatcher: {apiVersion: elasticache.services.k8s.aws/v1alpha1, kind: UserGroup}
+
+- supportsObservedGeneration: false
+ conditionMatchers:
+ - type: ReconcileSucceeded
+ status: "True"
+ success: true
+ resourceMatchers:
+ - apiVersionKindMatcher: {apiVersion: secretgen.carvel.dev/v1alpha1, kind: SecretTemplate}
+
+rebaseRules:
+- paths:
+ - [spec, atRestEncryptionEnabled]
+ - [spec, cacheNodeType]
+ - [spec, cacheSubnetGroupName]
+ - [spec, description]
+ - [spec, engine]
+ - [spec, replicationGroupID]
+ - [spec, securityGroupIDs]
+ - [spec, snapshotRetentionLimit]
+ - [spec, snapshotWindow]
+ - [spec, transitEncryptionEnabled]
+ - [spec, userGroupIDs]
+ type: copy
+ sources: [new, existing]
+ resourceMatchers:
+ - apiVersionKindMatcher: {apiVersion: elasticache.services.k8s.aws/v1alpha1, kind: ReplicationGroup}
diff --git a/packages/aws/carvel/elasticache/package-metadata.yml b/packages/aws/carvel/elasticache/package-metadata.yml
new file mode 100644
index 0000000..73eee60
--- /dev/null
+++ b/packages/aws/carvel/elasticache/package-metadata.yml
@@ -0,0 +1,17 @@
+apiVersion: data.packaging.carvel.dev/v1alpha1
+kind: PackageMetadata
+metadata:
+ name: elasticache.aws.ref.services.apps.tanzu.vmware.com
+spec:
+ categories:
+ - services
+ displayName: AWS Elasticache instances
+ longDescription: |
+ Helps you install an AWS Elasticache for Redis using ACK (https://github.com/aws-controllers-k8s/elasticache-controller).
+ You can follow the guide at https://vmware-tanzu.github.io/tanzu-application-platform-reference-service-packages/usecases/aws/prerequisites/ack/
+ to quickly find relevant information about how to install and configure ACK for Elasticache.
+ maintainers:
+ - name: The TAP Reference Packages Team
+ providerName: VMware
+ shortDescription: AWS Elasticache with ACK controller
+ supportDescription: Not supported - provided as reference package only
diff --git a/packages/azure/carvel/psql/config/00-schema.yml b/packages/azure/carvel/psql/config/00-schema.yml
new file mode 100644
index 0000000..351c32b
--- /dev/null
+++ b/packages/azure/carvel/psql/config/00-schema.yml
@@ -0,0 +1,140 @@
+#@data/values-schema
+
+---
+#@schema/title "ASO Controller Namespace"
+#@schema/desc "The Namespace where the Azure ASO controller is installed that should own this Azure RM resource"
+aso_controller_namespace: "azureserviceoperator-system"
+
+#@schema/title "ResourceName"
+#@schema/desc "Name for the resources"
+name: "aso-psql"
+
+#@schema/title "ResourceNamespace"
+#@schema/desc "Kubernetes namespace where the Azure resources will be created"
+namespace: ""
+
+#@schema/title "Create namespace flag"
+#@schema/desc "Whether to create the namespace for the resources or not"
+create_namespace: False
+
+#@schema/title "ResourceGroup"
+#@schema/desc "Azure ResourceGroup for the servers/database resources"
+resource_group:
+ #@schema/title "Name"
+ #@schema/desc "Azure ResourceGroup name"
+ name: "aso-psql"
+ #@schema/title "UseExisting"
+ #@schema/desc "Whether to use the existing Azure resource group or not"
+ use_existing: False
+ #@schema/title "Tags"
+ #@schema/desc "Tags to attach to the object"
+ #@schema/default []
+ tags:
+ -
+ #@schema/title "Key"
+ #@schema/desc "The name of the tag"
+ key: ""
+ #@schema/title "Value"
+ #@schema/desc "The value of the tag"
+ value: ""
+
+
+#@schema/title "Location"
+#@schema/desc "Location where the resources will be created"
+location: ""
+
+#@schema/title "FlexibleServer"
+#@schema/desc "FlexibleServer instance that will be created"
+server:
+
+ #@schema/title "Name"
+ #@schema/desc "Flexible Server name. It must be unique across all Azure postgres database instances. Only lowercase letters, digits and hyphens are allowed."
+ name: ""
+
+ #@schema/title "Version"
+ #@schema/desc "PostgreSQL version to deploy (only 11, 12 and 13 are currently supported"
+ version: "13"
+
+ #@schema/title "AdministratorName"
+ #@schema/desc "Username for the administrator user. It cannot be 'azure_superuser', 'azuresu', 'azure_pg_admin', 'sa', 'admin', 'administrator', 'root', 'guest', 'dbmanager', 'loginmanager', 'dbo', 'information_schema', 'sys', 'db_accessadmin', 'db_backupoperator', 'db_datareader', 'db_datawriter', 'db_ddladmin', 'db_denydatareader', 'db_denydatawriter', 'db_owner', 'db_securityadmin', 'public'."
+ administrator_name: "myadmin"
+
+ #@schema/title "InstanceType"
+ #@schema/desc "The type of the requested instance (follows the convention Standard_{VM name})"
+ instance_type: "Standard_D2s_v3"
+
+ #@schema/title "InstanceTier"
+ #@schema/desc "The tier of the requested instance (allowed: 'Burstable', 'GeneralPurpose' or 'Memory Optimized')"
+ instance_tier: "GeneralPurpose"
+
+ #@schema/title "InstanceStorageSizeGB"
+ #@schema/desc "The storage size for the instance in GB (allowed: from 32 to 16384)"
+ instance_storage_size_gb: 128
+
+ #@schema/title "Tags"
+ #@schema/desc "Tags to attach to the object"
+ #@schema/default []
+ tags:
+ -
+ #@schema/title "Key"
+ #@schema/desc "The name of the tag"
+ key: ""
+ #@schema/title "Value"
+ #@schema/desc "The value of the tag"
+ value: ""
+
+#@schema/title "Database"
+#@schema/desc "The database that will be created."
+database:
+
+ #@schema/title "Name"
+ #@schema/desc "Name of the database"
+ name: ""
+
+ #@schema/title "Tags"
+ #@schema/desc "Tags to attach to the object"
+ #@schema/default []
+ tags:
+ -
+ #@schema/title "Key"
+ #@schema/desc "The name of the tag"
+ key: ""
+ #@schema/title "Value"
+ #@schema/desc "The value of the tag"
+ value: ""
+
+#@schema/title "FirewallRules"
+#@schema/desc "List of firewall rules for exposing the Flexible Server. '0.0.0.0' for both startIpAddress and endIpAddress means it will be available from Azure (not the whole public Internet). Must be IPv4 format."
+#@schema/default []
+firewall_rules:
+
+ -
+ #@schema/title "StartIpAddress"
+ #@schema/desc "The starting IP address of the range"
+ startIpAddress: ""
+ #@schema/title "EndIpAddress"
+ #@schema/desc "The ending IP address of the range"
+ endIpAddress: ""
+ #@schema/title "Tags"
+ #@schema/desc "Tags to attach to the object"
+ #@schema/default []
+ tags:
+ -
+ #@schema/title "Key"
+ #@schema/desc "The name of the tag"
+ key: ""
+ #@schema/title "Value"
+ #@schema/desc "The value of the tag"
+ value: ""
+
+#@schema/title "GlobalTags"
+#@schema/desc "Tags to attach to all the resources"
+#@schema/default []
+global_tags:
+ -
+ #@schema/title "Key"
+ #@schema/desc "The name of the tag"
+ key: ""
+ #@schema/title "Value"
+ #@schema/desc "The value of the tag"
+ value: ""
diff --git a/packages/azure/carvel/psql/config/01-flexible-server.ytt.yml b/packages/azure/carvel/psql/config/01-flexible-server.ytt.yml
new file mode 100644
index 0000000..2e8263b
--- /dev/null
+++ b/packages/azure/carvel/psql/config/01-flexible-server.ytt.yml
@@ -0,0 +1,244 @@
+#@ load("@ytt:data", "data")
+#@ load("@ytt:assert", "assert")
+#@ load("@ytt:regexp", "regexp")
+#@ load("@ytt:ip", "ip")
+
+#@ def tags(resource_tags=[]):
+#@ tags_list = []
+#@ tags_list.extend(data.values.global_tags)
+#@ tags_list.extend(resource_tags)
+#@ tags_map = { x["key"]: x["value"] for x in tags_list if x["key"] != "" and x["value"] != "" }
+#@ return(tags_map)
+#@ end
+
+#@ flexibleServerK8SName = data.values.name
+#@ dbSecretName = data.values.name
+#@ resourceGroupK8SName = data.values.name
+#@ serviceAccountName = data.values.name + "-reader"
+#@ serviceAccountNamespace = data.values.namespace
+#@ roleName = data.values.name + "-reading"
+
+#@ firewallRules = data.values.firewall_rules
+#@ if len(firewallRules) == 0:
+#@ firewallRules = [{"startIpAddress": "0.0.0.0", "endIpAddress": "0.0.0.0", "tags": []}]
+#@ end
+
+#@ mandatory_fields = [ "name", "location", "aso_controller_namespace" ]
+#@ missing_fields = []
+#@ for field in mandatory_fields:
+#@ if len(data.values[field]) == 0:
+#@ missing_fields.append(field)
+#@ end
+#@ end
+#@ if data.values.resource_group.name == "":
+#@ missing_fields.append("resource_group.name")
+#@ end
+
+#@ databaseName = "name" in data.values.database and data.values.database.name or data.values.name
+
+#!@ if len(missing_fields) > 0:
+#!@ assert.fail("Missing values for mandatory fields '{}'".format(missing_fields))
+#!@ end
+#@ if not regexp.match("^Standard_", data.values.server.instance_type):
+#@ assert.fail("'instance_type' must follow the convention Standard_(VMname) (was: '{}')".format(data.values.server.instance_type))
+#@ end
+#@ if data.values.server.instance_tier not in ["Burstable", "GeneralPurpose", "Memory Optimized"]:
+#@ assert.fail("'instance_tier' must be in [\"Burstable\", \"GeneralPurpose\", \"Memory Optimized\"] (was: '{}')".format(data.values.server.instance_tier))
+#@ end
+#@ if data.values.server.instance_storage_size_gb < 32 or data.values.server.instance_storage_size_gb > 16384:
+#@ assert.fail("'instance_storage_size_gb' must be within 32 and 16384 (was: {})".format(data.values.server.instance_storage_size_gb))
+#@ end
+
+#@ if data.values.create_namespace and data.values.namespace:
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: #@ data.values.namespace
+#@ end
+---
+apiVersion: secretgen.k14s.io/v1alpha1
+kind: Password
+metadata:
+ name: #@ dbSecretName
+#@ if/end data.values.namespace:
+ namespace: #@ data.values.namespace
+spec:
+ length: 64
+ secretTemplate:
+ type: Opaque
+ stringData:
+ password: $(value)
+---
+apiVersion: resources.azure.com/v1beta20200601
+kind: ResourceGroup
+metadata:
+ name: #@ resourceGroupK8SName
+#@ if/end data.values.namespace:
+ namespace: #@ data.values.namespace
+ annotations:
+ serviceoperator.azure.com/operator-namespace: #@ data.values.aso_controller_namespace
+#@ if/end data.values.resource_group.use_existing:
+ serviceoperator.azure.com/reconcile-policy: skip
+spec:
+ azureName: #@ data.values.resource_group.name
+ location: #@ data.values.location
+ tags: #@ tags(data.values.resource_group.tags)
+---
+apiVersion: dbforpostgresql.azure.com/v1beta20210601
+kind: FlexibleServersDatabase
+metadata:
+ name: #@ data.values.name
+#@ if/end data.values.namespace:
+ namespace: #@ data.values.namespace
+spec:
+ azureName: #@ databaseName
+ owner:
+ name: #@ flexibleServerK8SName
+ charset: utf8
+ tags: #@ tags(data.values.database.tags)
+
+#@ fwRuleCounter = 0
+#@ for rule in firewallRules:
+#@ startAddr = ip.parse_addr(rule["startIpAddress"])
+#@ endAddr = ip.parse_addr(rule["endIpAddress"])
+---
+apiVersion: dbforpostgresql.azure.com/v1beta20210601
+kind: FlexibleServersFirewallRule
+metadata:
+ name: #@ data.values.name + "-" + str(fwRuleCounter)
+#@ if/end data.values.namespace:
+ namespace: #@ data.values.namespace
+spec:
+ owner:
+ name: #@ flexibleServerK8SName
+ startIpAddress: #@ startAddr.string()
+ endIpAddress: #@ endAddr.string()
+ tags: #@ tags(rule["tags"])
+#@ fwRuleCounter += 1
+#@ end
+---
+apiVersion: dbforpostgresql.azure.com/v1beta20210601
+kind: FlexibleServer
+metadata:
+ name: #@ flexibleServerK8SName
+#@ if/end data.values.namespace:
+ namespace: #@ data.values.namespace
+spec:
+ location: #@ data.values.location
+ azureName: #@ data.values.server.name or flexibleServerK8SName
+ owner:
+ name: #@ resourceGroupK8SName
+ version: #@ data.values.server.version
+ sku:
+ name: #@ data.values.server.instance_type
+ tier: #@ data.values.server.instance_tier
+ administratorLogin: #@ data.values.server.administrator_name
+ administratorLoginPassword:
+ name: #@ dbSecretName
+ key: password
+ storage:
+ storageSizeGB: #@ data.values.server.instance_storage_size_gb
+ tags: #@ tags(data.values.server.tags)
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: #@ serviceAccountName
+#@ if/end serviceAccountNamespace:
+ namespace: #@ serviceAccountNamespace
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ name: #@ roleName
+#@ if/end data.values.namespace:
+ namespace: #@ data.values.namespace
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - secrets
+ verbs:
+ - get
+ - list
+ - watch
+ resourceNames:
+ - #@ data.values.name
+- apiGroups:
+ - dbforpostgresql.azure.com
+ resources:
+ - flexibleservers
+ - flexibleserversdatabases
+ verbs:
+ - get
+ - list
+ - watch
+ resourceNames:
+ - #@ data.values.name
+- apiGroups:
+ - resources.azure.com
+ resources:
+ - resourcegroups
+ verbs:
+ - get
+ - list
+ - watch
+ resourceNames:
+ - #@ resourceGroupK8SName
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: #@ roleName
+#@ if/end data.values.namespace:
+ namespace: #@ data.values.namespace
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: #@ roleName
+subjects:
+- kind: ServiceAccount
+ name: #@ serviceAccountName
+#@ if/end serviceAccountNamespace:
+ namespace: #@ serviceAccountNamespace
+---
+apiVersion: secretgen.carvel.dev/v1alpha1
+kind: SecretTemplate
+metadata:
+ name: #@ data.values.name + "-bindable"
+#@ if/end data.values.namespace:
+ namespace: #@ data.values.namespace
+spec:
+ serviceAccountName: #@ serviceAccountName
+ inputResources:
+ - name: server
+ ref:
+ apiVersion: dbforpostgresql.azure.com/v1alpha1api20210601
+ kind: FlexibleServer
+ name: #@ flexibleServerK8SName
+ - name: db
+ ref:
+ apiVersion: dbforpostgresql.azure.com/v1alpha1api20210601
+ kind: FlexibleServersDatabase
+ name: #@ data.values.name
+ - name: creds
+ ref:
+ apiVersion: v1
+ kind: Secret
+ name: "$(.server.spec.administratorLoginPassword.name)"
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/component: #@ data.values.name
+ app.kubernetes.io/instance: "$(.server.metadata.name)"
+ services.apps.tanzu.vmware.com/class: azure-postgres
+ type: servicebinding.io/postgresql
+ stringData:
+ type: postgresql
+ port: "5432"
+ database: "$(.db.status.name)"
+ host: "$(.server.status.fullyQualifiedDomainName)"
+ username: "$(.server.status.administratorLogin)"
+ data:
+ password: "$(.creds.data.password)"
diff --git a/packages/azure/carvel/psql/config/99-kapp-config.yml b/packages/azure/carvel/psql/config/99-kapp-config.yml
new file mode 100644
index 0000000..7fff562
--- /dev/null
+++ b/packages/azure/carvel/psql/config/99-kapp-config.yml
@@ -0,0 +1,57 @@
+apiVersion: kapp.k14s.io/v1alpha1
+kind: Config
+minimumRequiredVersion: 0.48.0
+waitRules:
+- supportsObservedGeneration: false
+ ytt:
+ funcContractV1:
+ resource.star: |
+ def is_done(resource):
+ kind = resource.kind
+ name = resource.metadata.name
+
+ if not "status" in resource or not "conditions" in resource.status:
+ return {"done": False, "successful": False, "message": "Resource " + kind + "/" + name + " waiting for status"}
+ end
+
+ condition = resource.status.conditions[0]
+ reason = ""
+ if "reason" in condition:
+ reason = condition.reason
+ end
+
+ if repr(condition.type) == repr("Ready") and repr(reason) == repr("Succeeded"):
+ return {"done": True, "successful": True, "message": "Resource " + kind + "/" + name + " created"}
+ elif repr(condition.type) == repr("Ready") and repr(condition.status) == repr("False") and repr(condition.severity) == repr("Error"):
+ return {"done": True, "successful": False, "message": "Resource " + kind + "/" + name + " creation failed: " + reason}
+ else:
+ return {"done": False, "successful": False, "message": "Resource " + kind + "/" + name + " still being reconciled: " + reason}
+ end
+ end
+ resourceMatchers: &flexibleServerResources
+ - apiVersionKindMatcher: {apiVersion: resources.azure.com/v1beta20200601, kind: ResourceGroup}
+ - apiVersionKindMatcher: {apiVersion: dbforpostgresql.azure.com/v1beta20210601, kind: FlexibleServer}
+ - apiVersionKindMatcher: {apiVersion: dbforpostgresql.azure.com/v1beta20210601, kind: FlexibleServersDatabase}
+ - apiVersionKindMatcher: {apiVersion: dbforpostgresql.azure.com/v1beta20210601, kind: FlexibleServersFirewallRule}
+
+- supportsObservedGeneration: false
+ conditionMatchers:
+ - type: ReconcileSucceeded
+ status: "True"
+ success: true
+ resourceMatchers:
+ - apiVersionKindMatcher: {apiVersion: secretgen.carvel.dev/v1alpha1, kind: SecretTemplate}
+
+rebaseRules:
+- paths:
+ - [metadata, annotations, serviceoperator.azure.com/operator-namespace]
+ - [metadata, annotations, serviceoperator.azure.com/resource-id]
+ - [metadata, annotations, serviceoperator.azure.com/poller-resume-id]
+ - [metadata, annotations, serviceoperator.azure.com/poller-resume-token]
+ type: copy
+ sources: [new, existing]
+ resourceMatchers: *flexibleServerResources
+
+ownershipLabelRules:
+- path: [metadata, labels]
+ resourceMatchers: *flexibleServerResources
diff --git a/repository/packages/azure/psql/metadata.yml b/packages/azure/carvel/psql/package-metadata.yml
similarity index 58%
rename from repository/packages/azure/psql/metadata.yml
rename to packages/azure/carvel/psql/package-metadata.yml
index cd82ec2..632b120 100644
--- a/repository/packages/azure/psql/metadata.yml
+++ b/packages/azure/carvel/psql/package-metadata.yml
@@ -1,16 +1,15 @@
apiVersion: data.packaging.carvel.dev/v1alpha1
kind: PackageMetadata
metadata:
- creationTimestamp: null
- name: psql.azure.references.services.apps.tanzu.vmware.com
+ name: psql.azure.ref.services.apps.tanzu.vmware.com
spec:
categories:
- services
- displayName: Azure Flexible Server for Postgresql
+ displayName: Azure Flexible Server for PostgreSQL
longDescription: |
- Helps you install an Azure Flexible Server for Postgresql which can be bound to your application via a ServiceBinding.
+ Helps you install an Azure Flexible Server for PostgreSQL which can be bound to your application via a ServiceBinding.
maintainers:
- name: The TAP Reference Packages Team
providerName: VMware
- shortDescription: dbforpostgresql.azure.com
+ shortDescription: Azure PostgreSQL with ASO
supportDescription: Not supported - provided as reference package only
diff --git a/packages/azure/crossplane/mongodb/claim-examples/mongodb-32-claim-example.yml b/packages/azure/crossplane/mongodb/claim-examples/mongodb-32-claim-example.yml
new file mode 100644
index 0000000..d3d9990
--- /dev/null
+++ b/packages/azure/crossplane/mongodb/claim-examples/mongodb-32-claim-example.yml
@@ -0,0 +1,22 @@
+apiVersion: azure.ref.services.apps.tanzu.vmware.com/v1alpha1
+kind: MongoDBInstance
+metadata:
+ namespace: default
+ name: trp-cosmosdb-mongo-09
+spec:
+ compositionSelector:
+ matchLabels:
+ database: mongodb
+ parameters:
+ location: "West Europe"
+ mongodbVersion: "3.2"
+ capabilities:
+ - name: "EnableMongo"
+ - name: "mongoEnableDocLevelTTL"
+ publishConnectionDetailsTo:
+ name: trp-cosmosdb-mongo-bindable-09
+ configRef:
+ name: default
+ metadata:
+ labels:
+ services.apps.tanzu.vmware.com/class: azure-mongodb
\ No newline at end of file
diff --git a/packages/azure/crossplane/mongodb/claim-examples/mongodb-3x-claim-example.yml b/packages/azure/crossplane/mongodb/claim-examples/mongodb-3x-claim-example.yml
new file mode 100644
index 0000000..943e9ac
--- /dev/null
+++ b/packages/azure/crossplane/mongodb/claim-examples/mongodb-3x-claim-example.yml
@@ -0,0 +1,21 @@
+apiVersion: azure.ref.services.apps.tanzu.vmware.com/v1alpha1
+kind: MongoDBInstance
+metadata:
+ namespace: default
+ name: trp-cosmosdb-mongo-08
+spec:
+ compositionSelector:
+ matchLabels:
+ database: mongodb
+ parameters:
+ location: "West Europe"
+ capabilities:
+ - name: "EnableMongo"
+ - name: "mongoEnableDocLevelTTL"
+ publishConnectionDetailsTo:
+ name: trp-cosmosdb-mongo-bindable-08
+ configRef:
+ name: default
+ metadata:
+ labels:
+ services.apps.tanzu.vmware.com/class: azure-mongodb
\ No newline at end of file
diff --git a/packages/azure/crossplane/mongodb/claim-examples/mongodb-4x-claim-example.yml b/packages/azure/crossplane/mongodb/claim-examples/mongodb-4x-claim-example.yml
new file mode 100644
index 0000000..f2e2e54
--- /dev/null
+++ b/packages/azure/crossplane/mongodb/claim-examples/mongodb-4x-claim-example.yml
@@ -0,0 +1,22 @@
+apiVersion: azure.ref.services.apps.tanzu.vmware.com/v1alpha1
+kind: MongoDBInstance
+metadata:
+ namespace: default
+ name: trp-cosmosdb-mongo-09
+spec:
+ compositionSelector:
+ matchLabels:
+ database: mongodb
+ parameters:
+ location: "West Europe"
+ mongodbVersion: "4.2"
+ capabilities:
+ - name: "EnableMongo"
+ - name: "mongoEnableDocLevelTTL"
+ publishConnectionDetailsTo:
+ name: trp-cosmosdb-mongo-bindable-09
+ configRef:
+ name: default
+ metadata:
+ labels:
+ services.apps.tanzu.vmware.com/class: azure-mongodb
\ No newline at end of file
diff --git a/packages/azure/crossplane/mongodb/ytt/composition.ytt.yml b/packages/azure/crossplane/mongodb/ytt/composition.ytt.yml
new file mode 100644
index 0000000..3010c9d
--- /dev/null
+++ b/packages/azure/crossplane/mongodb/ytt/composition.ytt.yml
@@ -0,0 +1,118 @@
+#@ load("@ytt:data", "data")
+---
+apiVersion: apiextensions.crossplane.io/v1
+kind: Composition
+metadata:
+ name: #@ (data.values.xrd.claimNames.kind).lower()
+ labels:
+ crossplane.io/xrd: #@ data.values.xrd.names.plural + "." + data.values.xrd.group
+ provider: #@ data.values.provider.name
+ database: #@ data.values.cloudServiceBindingType
+spec:
+ publishConnectionDetailsWithStoreConfigRef:
+ name: #@ data.values.storeConfig.name
+ compositeTypeRef:
+ apiVersion: #@ data.values.xrd.group + "/" + data.values.xrd.version
+ kind: #@ data.values.xrd.names.kind
+ resources:
+ - name: resourcegroup
+ base:
+ apiVersion: azure.upbound.io/v1beta1
+ kind: ResourceGroup
+ spec: {}
+ patches:
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.location
+ - name: account
+ base:
+ apiVersion: cosmosdb.azure.upbound.io/v1beta1
+ kind: Account
+ spec:
+ forProvider:
+ consistencyPolicy:
+ - consistencyLevel: Strong
+ geoLocation:
+ - failoverPriority: 0
+ kind: MongoDB
+ offerType: Standard
+ resourceGroupNameSelector:
+ matchControllerRef: true
+ providerConfigRef:
+ name: #@ data.values.provider.configRef
+ writeConnectionSecretToRef:
+ namespace: #@ data.values.crossplane.namespace
+ patches:
+ - type: FromCompositeFieldPath
+ fromFieldPath: metadata.uid
+ toFieldPath: spec.writeConnectionSecretToRef.name
+ transforms:
+ - type: string
+ string:
+ fmt: "%s-mongodb"
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.capabilities
+ toFieldPath: spec.forProvider.capabilities
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.mongodbVersion
+ toFieldPath: spec.forProvider.mongoServerVersion
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.location
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.geoLocation[0].location
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.providerConfig
+ toFieldPath: spec.providerConfigRef.name
+ - type: ToCompositeFieldPath
+ fromFieldPath: spec.forProvider.mongoServerVersion
+ toFieldPath: status.mongodbVersion
+ - type: ToCompositeFieldPath
+ fromFieldPath: spec.forProvider.location
+ toFieldPath: status.location
+ - type: ToCompositeFieldPath
+ fromFieldPath: status.atProvider.endpoint
+ toFieldPath: status.endpoint
+ connectionDetails:
+ - name: uri
+ fromConnectionSecretKey: "attribute.connection_strings.0"
+ - type: FromValue
+ name: type
+ value: #@ data.values.cloudServiceBindingType
+ - name: mongodatabase
+ base:
+ apiVersion: cosmosdb.azure.upbound.io/v1beta1
+ kind: MongoDatabase
+ spec:
+ forProvider:
+ accountNameSelector:
+ matchControllerRef: true
+ resourceGroupNameSelector:
+ matchControllerRef: true
+ providerConfigRef:
+ name: #@ data.values.provider.configRef
+ patches:
+ connectionDetails:
+ - type: FromFieldPath
+ name: database
+ fromFieldPath: metadata.name
+ - name: mongocollection
+ base:
+ apiVersion: cosmosdb.azure.upbound.io/v1beta1
+ kind: MongoCollection
+ spec:
+ forProvider:
+ accountNameSelector:
+ matchControllerRef: true
+ databaseNameSelector:
+ matchControllerRef: true
+ resourceGroupNameSelector:
+ matchControllerRef: true
+ defaultTtlSeconds: 777
+ index:
+ - keys:
+ - _id
+ unique: true
+ shardKey: uniqueKey
+ throughput: 400
diff --git a/packages/azure/crossplane/mongodb/ytt/crossplane.ytt.yml b/packages/azure/crossplane/mongodb/ytt/crossplane.ytt.yml
new file mode 100644
index 0000000..1b95d20
--- /dev/null
+++ b/packages/azure/crossplane/mongodb/ytt/crossplane.ytt.yml
@@ -0,0 +1,12 @@
+#@ load("@ytt:data", "data")
+---
+apiVersion: meta.pkg.crossplane.io/v1
+kind: Configuration
+metadata:
+ name: #@ data.values.configurationName
+ annotations:
+ provider: #@ data.values.provider.name
+spec:
+ dependsOn:
+ - provider: #@ data.values.provider.image
+ version: #@ data.values.provider.version
\ No newline at end of file
diff --git a/packages/azure/crossplane/mongodb/ytt/definition.ytt.yml b/packages/azure/crossplane/mongodb/ytt/definition.ytt.yml
new file mode 100644
index 0000000..95facbd
--- /dev/null
+++ b/packages/azure/crossplane/mongodb/ytt/definition.ytt.yml
@@ -0,0 +1,76 @@
+#@ load("@ytt:data", "data")
+---
+apiVersion: apiextensions.crossplane.io/v1
+kind: CompositeResourceDefinition
+metadata:
+ name: #@ data.values.xrd.names.plural + "." + data.values.xrd.group
+spec:
+ group: #@ data.values.xrd.group
+ names: #@ data.values.xrd.names
+ claimNames: #@ data.values.xrd.claimNames
+ connectionSecretKeys:
+ - database
+ - uri
+ - type
+ versions:
+ - name: v1alpha1
+ served: true
+ referenceable: true
+ schema:
+ openAPIV3Schema:
+ type: object
+ properties:
+ spec:
+ type: object
+ properties:
+ parameters:
+ type: object
+ properties:
+ location:
+ type: string
+ providerConfig:
+ type: string
+ default: default
+ mongodbVersion:
+ type: string
+ default: "3.6"
+ enum: ["4.2", "4.0", "3.6", "3.2"]
+ capabilities:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ required:
+ - location
+ - capabilities
+ required:
+ - parameters
+ status:
+ type: object
+ properties:
+ mongodbVersion:
+ description: The version of the MongoDB server
+ type: string
+ location:
+ description: The location of the MongoDB server
+ type: string
+ endpoint:
+ description: The endpoint of the MongoDB server
+ type: string
+ additionalPrinterColumns:
+ - name: mongodbVersion
+ type: string
+ jsonPath: ".status.mongodbVersion"
+ - name: location
+ type: string
+ jsonPath: ".status.location"
+ - name: endpoint
+ type: string
+ jsonPath: ".status.endpoint"
+ - name: connection-details
+ type: string
+ jsonPath: ".spec.publishConnectionDetailsTo.name"
\ No newline at end of file
diff --git a/packages/azure/crossplane/mongodb/ytt/schema.ytt.yml b/packages/azure/crossplane/mongodb/ytt/schema.ytt.yml
new file mode 100644
index 0000000..508582c
--- /dev/null
+++ b/packages/azure/crossplane/mongodb/ytt/schema.ytt.yml
@@ -0,0 +1,34 @@
+#@data/values-schema
+---
+
+xrd:
+ group: azure.ref.services.apps.tanzu.vmware.com
+ names:
+ kind: XMongoDBInstance
+ plural: xmongodbinstances
+ claimNames:
+ kind: MongoDBInstance
+ plural: mongodbinstances
+ version: v1alpha1
+
+configurationName: "azure-mongodb"
+cloudServiceBindingType: "mongodb"
+
+provider:
+ name: azure
+ image: xpkg.upbound.io/upbound/provider-azure
+ version: ">=v0.18.1"
+ configRef: default
+
+crossplane:
+ #@schema/title "CrossplaneNamespace"
+ #@schema/desc "The namespace where crossplane controller is installed"
+ namespace: upbound-system
+ version: '^v1.10'
+
+#@schema/title "StoreConfig"
+#@schema/desc "Details of the StoreConfig"
+storeConfig:
+ #@schema/title "StoreConfig Name"
+ #@schema/desc "The name of the StoreConfig"
+ name: "default"
diff --git a/packages/multicloud/crossplane/psql/README.md b/packages/multicloud/crossplane/psql/README.md
new file mode 100644
index 0000000..fc0b6be
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/README.md
@@ -0,0 +1,14 @@
+# Multicloud PSQL - Crossplane
+
+## Azure
+
+### Pre-requisites
+
+* Kubernetes cluster
+* Azure Connection Details
+* SecretGen Controller
+
+
+## Local (Kubernetes)
+
+## AWS?
diff --git a/packages/multicloud/crossplane/psql/claim-examples/aws-private.yaml b/packages/multicloud/crossplane/psql/claim-examples/aws-private.yaml
new file mode 100644
index 0000000..18f1e17
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/claim-examples/aws-private.yaml
@@ -0,0 +1,22 @@
+apiVersion: multi.ref.services.apps.tanzu.vmware.com/v1alpha1
+kind: PostgreSQLInstance
+metadata:
+ name: aws-psql-private-01
+ labels:
+ services.apps.tanzu.vmware.com/claimable: "true"
+spec:
+ compositionSelector:
+ matchLabels:
+ provider: aws
+ connectivity: "private"
+ parameters:
+ location: eu-central-1
+ version: "12"
+ database: demo
+ collation: en_GB.utf8
+ storageClass: gp2
+ aws:
+ vpcId: vpc-0157e2424963dcd93
+ dbSubnetGroupName: trp-testing
+ cidrBlocks:
+ - 0.0.0.0/0
\ No newline at end of file
diff --git a/packages/multicloud/crossplane/psql/claim-examples/aws-public.yaml b/packages/multicloud/crossplane/psql/claim-examples/aws-public.yaml
new file mode 100644
index 0000000..e1b7f62
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/claim-examples/aws-public.yaml
@@ -0,0 +1,26 @@
+apiVersion: multi.ref.services.apps.tanzu.vmware.com/v1alpha1
+kind: PostgreSQLInstance
+metadata:
+ name: aws-psql-public-01
+ labels:
+ services.apps.tanzu.vmware.com/claimable: "true"
+spec:
+ compositionSelector:
+ matchLabels:
+ provider: aws
+ connectivity: "public"
+ parameters:
+ location: eu-central-1
+ version: "12"
+ database: demo
+ collation: en_GB.utf8
+ storageClass: gp2
+ aws:
+ vpcId: vpc-0157e2424963dcd93
+ dbSubnetGroupName: trp-testing
+ cidrBlocks:
+ - 0.0.0.0/0
+ public:
+ gatewayId: igw-03e173382a35db8ab
+ subnetACidrBlock: "10.100.255.0/25"
+ subnetBCidrBlock: "10.100.255.128/25"
diff --git a/packages/multicloud/crossplane/psql/claim-examples/azure-psql.yaml b/packages/multicloud/crossplane/psql/claim-examples/azure-psql.yaml
new file mode 100644
index 0000000..6c28f05
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/claim-examples/azure-psql.yaml
@@ -0,0 +1,19 @@
+apiVersion: multi.ref.services.apps.tanzu.vmware.com/v1alpha1
+kind: PostgreSQLInstance
+metadata:
+ name: my-azure-psql
+ labels:
+ services.apps.tanzu.vmware.com/claimable: "true"
+spec:
+ compositionSelector:
+ matchLabels:
+ provider: azure
+ parameters:
+ location: "West Europe"
+ version: "12"
+ database: demo
+ collation: en_GB.utf8
+ storageClass: hostpath
+ firewallRule:
+ startIpAddress: "0.0.0.0"
+ endIpAddress: "255.255.255.255"
\ No newline at end of file
diff --git a/packages/multicloud/crossplane/psql/claim-examples/helm-psql-12.yaml b/packages/multicloud/crossplane/psql/claim-examples/helm-psql-12.yaml
new file mode 100644
index 0000000..4270056
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/claim-examples/helm-psql-12.yaml
@@ -0,0 +1,16 @@
+apiVersion: multi.ref.services.apps.tanzu.vmware.com/v1alpha1
+kind: PostgreSQLInstance
+metadata:
+ name: postgresql-049
+ labels:
+ services.apps.tanzu.vmware.com/claimable: "true"
+spec:
+ compositionSelector:
+ matchLabels:
+ provider: helm
+ parameters:
+ location: local
+ version: "12"
+ database: petclinic
+ collation: en_GB.utf8
+ storageClass: gp2
diff --git a/packages/multicloud/crossplane/psql/claim/helm.yml b/packages/multicloud/crossplane/psql/claim/helm.yml
new file mode 100644
index 0000000..b1caef7
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/claim/helm.yml
@@ -0,0 +1,18 @@
+#@ load("@ytt:data", "data")
+---
+apiVersion: #@ data.values.xrd.group + "/" + data.values.xrd.version
+kind: #@ data.values.xrd.claimNames.kind
+metadata:
+ name: postgresql-049
+ labels:
+ services.apps.tanzu.vmware.com/claimable: "true"
+spec:
+ compositionSelector:
+ matchLabels:
+ provider: helm
+ parameters:
+ location: local
+ version: #@ data.values.version
+ database: petclinic
+ collation: en_GB.utf8
+ storageClass: gp2
diff --git a/packages/multicloud/crossplane/psql/ytt/aws-composition-private.ytt.yml b/packages/multicloud/crossplane/psql/ytt/aws-composition-private.ytt.yml
new file mode 100644
index 0000000..b43fcc3
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/ytt/aws-composition-private.ytt.yml
@@ -0,0 +1,27 @@
+#@ load("@ytt:data", "data")
+---
+#@ load("aws-composition.lib.yml", "rdsInstance", "securityGroup", "securityGroupRule")
+#@ load("shared.lib.yml", "labelsForSecret", "tfProviderConfig", "tfWorkspace")
+
+apiVersion: apiextensions.crossplane.io/v1
+kind: Composition
+metadata:
+ name: #@ data.values.providers.aws.name + "-" + data.values.cloudServiceBindingType + "-private"
+ labels:
+ crossplane.io/xrd: #@ data.values.xrd.names.plural + "." + data.values.xrd.group
+ provider: #@ data.values.providers.aws.name
+ database: #@ data.values.cloudServiceBindingType
+ connectivity: "private"
+spec:
+ writeConnectionSecretsToNamespace: #@ data.values.crossplane.namespace
+ compositeTypeRef:
+ apiVersion: #@ data.values.xrd.group + "/" + data.values.xrd.version
+ kind: #@ data.values.xrd.names.kind
+ resources:
+ - #@ labelsForSecret("aws")
+ - #@ tfProviderConfig(data.values.crossplane.namespace)
+ - #@ tfWorkspace(data.values.crossplane.namespace)
+ - #@ rdsInstance(data.values.crossplane.namespace, data.values.providers.aws.configRef, False)
+ - #@ securityGroup()
+ - #@ securityGroupRule()
+
diff --git a/packages/multicloud/crossplane/psql/ytt/aws-composition-public.ytt.yml b/packages/multicloud/crossplane/psql/ytt/aws-composition-public.ytt.yml
new file mode 100644
index 0000000..68d16ed
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/ytt/aws-composition-public.ytt.yml
@@ -0,0 +1,43 @@
+#@ load("@ytt:data", "data")
+---
+#@ load("aws-composition.lib.yml", "rdsInstance", "securityGroup", "securityGroupRule", "subnet", "routeTableAssociation", "routeTable", "route", "subnetGroup")
+#@ load("shared.lib.yml", "labelsForSecret", "tfProviderConfig", "tfWorkspace")
+
+#@ subnetASuffix = '-a'
+#@ subnetAFormat = 'subnet-a-%s'
+#@ availabilityZoneAFormat = '%sa'
+#@ cidrBlockFieldA = 'spec.parameters.aws.public.subnetACidrBlock'
+
+#@ subnetBSuffix = '-b'
+#@ subnetBFormat = 'subnet-b-%s'
+#@ availabilityZoneBFormat = '%sb'
+#@ cidrBlockFieldB = 'spec.parameters.aws.public.subnetBCidrBlock'
+
+apiVersion: apiextensions.crossplane.io/v1
+kind: Composition
+metadata:
+ name: #@ data.values.providers.aws.name + "-" + data.values.cloudServiceBindingType + "-public"
+ labels:
+ crossplane.io/xrd: #@ data.values.xrd.names.plural + "." + data.values.xrd.group
+ provider: #@ data.values.providers.aws.name
+ database: #@ data.values.cloudServiceBindingType
+ connectivity: "public"
+spec:
+ writeConnectionSecretsToNamespace: #@ data.values.crossplane.namespace
+ compositeTypeRef:
+ apiVersion: #@ data.values.xrd.group + "/" + data.values.xrd.version
+ kind: #@ data.values.xrd.names.kind
+ resources:
+ - #@ labelsForSecret("aws")
+ - #@ tfProviderConfig(data.values.crossplane.namespace)
+ - #@ tfWorkspace(data.values.crossplane.namespace)
+ - #@ rdsInstance(data.values.crossplane.namespace, data.values.providers.aws.configRef, True)
+ - #@ securityGroup()
+ - #@ securityGroupRule()
+ - #@ routeTable()
+ - #@ subnetGroup()
+ - #@ subnet(subnetASuffix, subnetAFormat, availabilityZoneAFormat, cidrBlockFieldA)
+ - #@ routeTableAssociation(subnetASuffix, subnetAFormat)
+ - #@ subnet(subnetBSuffix, subnetBFormat, availabilityZoneBFormat, cidrBlockFieldB)
+ - #@ routeTableAssociation(subnetBSuffix, subnetBFormat)
+ - #@ route('route-%s')
\ No newline at end of file
diff --git a/packages/multicloud/crossplane/psql/ytt/aws-composition.lib.yml b/packages/multicloud/crossplane/psql/ytt/aws-composition.lib.yml
new file mode 100644
index 0000000..fe5b77b
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/ytt/aws-composition.lib.yml
@@ -0,0 +1,293 @@
+#@ def subnetGroupNameMatcher():
+
+#@ end
+
+---
+#@ def rdsInstance(crossplaneNamespace, providerConfigRef, publiclyAccessible):
+name: rdsinstance
+base:
+ apiVersion: rds.aws.upbound.io/v1beta1
+ kind: Instance
+ spec:
+ forProvider:
+ engine: postgres
+ instanceClass: db.t3.micro
+ passwordSecretRef:
+ key: password
+ namespace: #@ crossplaneNamespace
+ publiclyAccessible: #@ publiclyAccessible
+ skipFinalSnapshot: true
+ storageEncrypted: false
+ allocatedStorage: 10
+ vpcSecurityGroupIdSelector:
+ matchControllerRef: true
+ #@ if/end publiclyAccessible:
+ dbSubnetGroupNameSelector:
+ matchControllerRef: true
+ providerConfigRef:
+ name: #@ providerConfigRef
+patches:
+#@ if/end not publiclyAccessible:
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.aws.dbSubnetGroupName
+ toFieldPath: spec.forProvider.dbSubnetGroupName
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.uid
+ toFieldPath: spec.forProvider.passwordSecretRef.name
+ transforms:
+ - type: string
+ string:
+ type: Format
+ fmt: '%s-postgresql-admin'
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.version
+ toFieldPath: spec.forProvider.engineVersion
+ transforms:
+ - type: map
+ map:
+ "11": "11.17"
+ "12": "12.12"
+ "13": "13.8"
+ "14": "14.5"
+ "15": "14.5"
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.region
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.adminUsername
+ toFieldPath: spec.forProvider.username
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.database
+ toFieldPath: spec.forProvider.dbName
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.storageClass
+ toFieldPath: spec.forProvider.storageType
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.collation
+ toFieldPath: spec.forProvider.collation
+connectionDetails:
+- name: type
+ value: postgresql
+- name: provider
+ value: aws
+- name: port
+ value: "5432"
+- name: database
+ fromFieldPath: spec.forProvider.dbName
+ type: FromFieldPath
+- name: username
+ fromFieldPath: spec.forProvider.username
+ type: FromFieldPath
+- name: host
+ fromFieldPath: status.atProvider.address
+ type: FromFieldPath
+- name: securitygroup
+#@ end
+
+---
+#@ def securityGroup():
+name: securitygroup
+base:
+ apiVersion: ec2.aws.upbound.io/v1beta1
+ kind: SecurityGroup
+patches:
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.name
+ toFieldPath: spec.forProvider.name
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.name
+ toFieldPath: spec.forProvider.description
+ transforms:
+ - string:
+ fmt: 'Traffic to RDS instance %s'
+ type: Format
+ type: string
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.aws.vpcId
+ toFieldPath: spec.forProvider.vpcId
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.region
+#@ end
+
+---
+#@ def securityGroupRule():
+name: securitygrouprule
+base:
+ apiVersion: ec2.aws.upbound.io/v1beta1
+ kind: SecurityGroupRule
+ spec:
+ forProvider:
+ fromPort: 5432
+ toPort: 5432
+ protocol: tcp
+ securityGroupIdSelector:
+ matchControllerRef: true
+ type: ingress
+patches:
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.aws.cidrBlocks
+ toFieldPath: spec.forProvider.cidrBlocks
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.region
+#@ end
+
+---
+#@ def subnetGroup():
+name: subnetgroup
+base:
+ apiVersion: rds.aws.upbound.io/v1beta1
+ kind: SubnetGroup
+ spec:
+ forProvider:
+ subnetIdSelector:
+ matchControllerRef: true
+ tags:
+ Name: subnet group for RDS via crossplane
+patches:
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.uid
+ toFieldPath: metadata.name
+ transforms:
+ - type: string
+ string:
+ type: Format
+ fmt: 'rds-sg-%s'
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.region
+#@ end
+
+---
+#@ def subnet(nameSuffix, nameFormat, availabilityZoneFormat, cidrBlockField):
+name: #@ "subnet" + nameSuffix
+base:
+ apiVersion: ec2.aws.upbound.io/v1beta1
+ kind: Subnet
+patches:
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.aws.vpcId
+ toFieldPath: spec.forProvider.vpcId
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.region
+- type: FromCompositeFieldPath
+ fromFieldPath: #@ cidrBlockField
+ toFieldPath: spec.forProvider.cidrBlock
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.uid
+ toFieldPath: metadata.name
+ transforms:
+ - type: string
+ string:
+ type: Format
+ fmt: #@ nameFormat
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.uid
+ toFieldPath: spec.forProfider.tags.Name
+ transforms:
+ - type: string
+ string:
+ type: Format
+ fmt: #@ nameFormat
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.availabilityZone
+ transforms:
+ - type: string
+ string:
+ type: Format
+ fmt: #@ availabilityZoneFormat
+#@ end
+
+---
+#@ def routeTable():
+name: routetable
+base:
+ apiVersion: ec2.aws.upbound.io/v1beta1
+ kind: RouteTable
+ metadata:
+ name: table
+ spec:
+ forProvider:
+ tags:
+ Name: RDS-via-Crossplane
+patches:
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.aws.vpcId
+ toFieldPath: spec.forProvider.vpcId
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.region
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.uid
+ toFieldPath: metadata.name
+ transforms:
+ - type: string
+ string:
+ type: Format
+ fmt: 'rds-%s'
+#@ end
+
+---
+#@ def routeTableAssociation(nameSuffix, subnetNameFormat):
+name: #@ "routeTableAssociation" + nameSuffix
+base:
+ apiVersion: ec2.aws.upbound.io/v1beta1
+ kind: RouteTableAssociation
+ spec:
+ forProvider:
+ routeTableIdSelector:
+ matchControllerRef: true
+patches:
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.region
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.uid
+ toFieldPath: metadata.name
+ transforms:
+ - type: string
+ string:
+ type: Format
+ fmt: #@ subnetNameFormat
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.uid
+ toFieldPath: spec.forProvider.subnetIdRef.name
+ transforms:
+ - type: string
+ string:
+ type: Format
+ fmt: #@ subnetNameFormat
+#@ end
+
+---
+#@ def route(routeFormat):
+name: route
+base:
+ apiVersion: ec2.aws.upbound.io/v1beta1
+ kind: Route
+ metadata:
+ name: route
+ spec:
+ forProvider:
+ destinationCidrBlock: 0.0.0.0/0
+ routeTableIdSelector:
+ matchControllerRef: true
+patches:
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.region
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.aws.public.gatewayId
+ toFieldPath: spec.forProvider.gatewayId
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.uid
+ toFieldPath: metadata.name
+ transforms:
+ - type: string
+ string:
+ type: Format
+ fmt: #@ routeFormat
+#@ end
\ No newline at end of file
diff --git a/packages/multicloud/crossplane/psql/ytt/azure-composition.ytt.yml b/packages/multicloud/crossplane/psql/ytt/azure-composition.ytt.yml
new file mode 100644
index 0000000..27693c8
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/ytt/azure-composition.ytt.yml
@@ -0,0 +1,141 @@
+#@ load("@ytt:data", "data")
+---
+
+#@ load("shared.lib.yml", "labelsForSecret", "tfProviderConfig", "tfWorkspace")
+
+apiVersion: apiextensions.crossplane.io/v1
+kind: Composition
+metadata:
+ name: #@ data.values.providers.azure.name + "-" + data.values.cloudServiceBindingType
+ labels:
+ crossplane.io/xrd: #@ data.values.xrd.names.plural + "." + data.values.xrd.group
+ provider: #@ data.values.providers.azure.name
+ database: #@ data.values.cloudServiceBindingType
+spec:
+ writeConnectionSecretsToNamespace: #@ data.values.crossplane.namespace
+ compositeTypeRef:
+ apiVersion: #@ data.values.xrd.group + "/" + data.values.xrd.version
+ kind: #@ data.values.xrd.names.kind
+ resources:
+ - #@ labelsForSecret("azure")
+ - #@ tfProviderConfig(data.values.crossplane.namespace)
+ - #@ tfWorkspace(data.values.crossplane.namespace)
+ - name: resourcegroup
+ base:
+ apiVersion: azure.upbound.io/v1beta1
+ kind: ResourceGroup
+ spec: {}
+ patches:
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.location
+ - type: ToCompositeFieldPath
+ fromFieldPath: metadata.name
+ toFieldPath: status.resourceGroup
+ - name: flexibleserver
+ base:
+ apiVersion: dbforpostgresql.azure.upbound.io/v1beta1
+ kind: FlexibleServer
+ spec:
+ providerConfigRef:
+ name: #@ data.values.providers.azure.configRef
+ forProvider:
+ resourceGroupNameSelector:
+ matchControllerRef: true
+ administratorLogin: psqladmin
+ skuName: GP_Standard_D4s_v3
+ storageMb: 32768
+ administratorPasswordSecretRef:
+ key: password
+ name: ""
+ namespace: #@ data.values.crossplane.namespace
+ location: ""
+ version: "" #! 11,12 and 13 are supported
+ patches:
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.location
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.version
+ toFieldPath: spec.forProvider.version
+ transforms:
+ - type: map
+ map:
+ "11": "11"
+ "12": "12"
+ "13": "13"
+ "14": "13"
+ "15": "13"
+ - type: FromCompositeFieldPath
+ fromFieldPath: metadata.uid
+ toFieldPath: spec.forProvider.administratorPasswordSecretRef.name
+ transforms:
+ - string:
+ fmt: '%s-postgresql-admin'
+ type: Format
+ type: string
+ connectionDetails:
+ - name: type
+ value: postgresql
+ - name: provider
+ value: azure
+ - name: database
+ value: postgres
+ - name: username
+ fromFieldPath: spec.forProvider.administratorLogin
+ - name: host
+ fromFieldPath: status.atProvider.fqdn
+ - name: port
+ type: FromValue
+ value: "5432"
+ - name: flexibleserverconfig
+ base:
+ apiVersion: dbforpostgresql.azure.upbound.io/v1beta1
+ kind: FlexibleServerConfiguration
+ spec:
+ providerConfigRef:
+ name: #@ data.values.providers.azure.configRef
+ forProvider:
+ serverIdSelector:
+ matchControllerRef: true
+ name: backslash_quote
+ value: "on"
+ - name: flexibleserverdatabase
+ base:
+ apiVersion: dbforpostgresql.azure.upbound.io/v1beta1
+ kind: FlexibleServerDatabase
+ spec:
+ providerConfigRef:
+ name: #@ data.values.providers.azure.configRef
+ forProvider:
+ serverIdSelector:
+ matchControllerRef: true
+ charset: ""
+ collation: ""
+ patches:
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.charset
+ toFieldPath: spec.forProvider.charset
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.collation
+ toFieldPath: spec.forProvider.collation
+ - name: firewallrule
+ base:
+ apiVersion: dbforpostgresql.azure.upbound.io/v1beta1
+ kind: FlexibleServerFirewallRule
+ spec:
+ providerConfigRef:
+ name: #@ data.values.providers.azure.configRef
+ forProvider:
+ serverIdSelector:
+ matchControllerRef: true
+ startIpAddress: ""
+ endIpAddress: ""
+ patches:
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.firewallRule.startIpAddress
+ toFieldPath: spec.forProvider.startIpAddress
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.firewallRule.endIpAddress
+ toFieldPath: spec.forProvider.endIpAddress
+
diff --git a/packages/multicloud/crossplane/psql/ytt/crossplane.ytt.yml b/packages/multicloud/crossplane/psql/ytt/crossplane.ytt.yml
new file mode 100644
index 0000000..7f88c1c
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/ytt/crossplane.ytt.yml
@@ -0,0 +1,13 @@
+#@ load("@ytt:data", "data")
+---
+apiVersion: meta.pkg.crossplane.io/v1
+kind: Configuration
+metadata:
+ name: #@ data.values.configurationName
+spec:
+ dependsOn:
+ #@ for name in data.values.providers:
+ #@ provider = data.values.providers[name]
+ - provider: #@ provider.image
+ version: #@ provider.version
+ #@ end
diff --git a/packages/multicloud/crossplane/psql/ytt/definition.ytt.yml b/packages/multicloud/crossplane/psql/ytt/definition.ytt.yml
new file mode 100644
index 0000000..a2e51c5
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/ytt/definition.ytt.yml
@@ -0,0 +1,133 @@
+#@ load("@ytt:data", "data")
+---
+apiVersion: apiextensions.crossplane.io/v1
+kind: CompositeResourceDefinition
+metadata:
+ name: #@ data.values.xrd.names.plural + "." + data.values.xrd.group
+spec:
+ group: #@ data.values.xrd.group
+ names: #@ data.values.xrd.names
+ claimNames: #@ data.values.xrd.claimNames
+ connectionSecretKeys:
+ - type
+ - provider
+ - host
+ - port
+ - database
+ - username
+ - password
+ versions:
+
+ - name: v1alpha1
+ served: true
+ referenceable: true
+ schema:
+ openAPIV3Schema:
+ type: object
+ properties:
+ spec:
+ type: object
+ required:
+ - parameters
+ properties:
+ parameters:
+ type: object
+ required:
+ - location
+ properties:
+ location:
+ type: string
+ version:
+ type: string
+ default: "13"
+ enum: ["11", "12", "13", "14", "15"]
+ adminUsername:
+ type: string
+ default: postgres
+ storageClass:
+ type: string
+ default: default
+ database:
+ type: string
+ default: postgres
+ collation:
+ type: string
+ default: en_US.utf8
+ charset:
+ type: string
+ default: utf8
+ firewallRule:
+ type: object
+ properties:
+ startIpAddress:
+ type: string
+ endIpAddress:
+ type: string
+ aws:
+ type: object
+ description: properties for AWS only
+ required:
+ - cidrBlocks
+ - dbSubnetGroupName
+ - vpcId
+ properties:
+ cidrBlocks:
+ type: array
+ description: cidr blocks for AWS security group
+ default:
+ - "0.0.0.0/0"
+ items:
+ type: string
+ pattern: '^(1?\d{1,2}|2[01234]\d|25[012345])(\.(1?\d{1,2}|2[01234]\d|25[012345])){3}\/(\d|[12]\d|3[012])$'
+ dbSubnetGroupName:
+ type: string
+ description: name for the AWS subnet group
+ vpcId:
+ type: string
+ description: id of the existing vpc
+ public:
+ type: object
+ description: set these if you use the AWS public composition
+ required:
+ - gatewayId
+ - subnetACidrBlock
+ - subnetBCidrBlock
+ properties:
+ gatewayId:
+ type: string
+ description: id of the existing gateway
+ subnetACidrBlock:
+ type: string
+ description: cidr block for the subnet a (creates two subnets, a and b)
+ default: "10.100.255.0/25"
+ subnetBCidrBlock:
+ type: string
+ description: cidr block for the subnet b (creates two subnets, a and b)
+ default: "10.100.255.128/25"
+ status:
+ type: object
+ properties:
+ version:
+ type: string
+ address:
+ type: string
+ location:
+ type: string
+ binding:
+ type: object
+ properties:
+ name:
+ type: string
+ additionalPrinterColumns:
+ - name: address
+ type: string
+ jsonPath: .status.address
+ - name: location
+ type: string
+ jsonPath: .status.location
+ - name: version
+ type: string
+ jsonPath: .status.version
+ - name: connection-details
+ type: string
+ jsonPath: .status.binding.name
\ No newline at end of file
diff --git a/packages/multicloud/crossplane/psql/ytt/helm-composition.ytt.yml b/packages/multicloud/crossplane/psql/ytt/helm-composition.ytt.yml
new file mode 100644
index 0000000..24085ef
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/ytt/helm-composition.ytt.yml
@@ -0,0 +1,118 @@
+#@ load("@ytt:data", "data")
+---
+
+#@ load("shared.lib.yml", "labelsForSecret", "tfProviderConfig", "tfWorkspace")
+
+apiVersion: apiextensions.crossplane.io/v1
+kind: Composition
+metadata:
+ name: #@ data.values.providers.helm.name + "-" + data.values.cloudServiceBindingType
+ labels:
+ crossplane.io/xrd: #@ data.values.xrd.names.plural + "." + data.values.xrd.group
+ provider: #@ data.values.providers.helm.name
+ database: #@ data.values.cloudServiceBindingType
+spec:
+ writeConnectionSecretsToNamespace: #@ data.values.crossplane.namespace
+ compositeTypeRef:
+ apiVersion: #@ data.values.xrd.group + "/" + data.values.xrd.version
+ kind: #@ data.values.xrd.names.kind
+ resources:
+ - #@ labelsForSecret("kubernetes")
+ - #@ tfProviderConfig(data.values.crossplane.namespace)
+ - #@ tfWorkspace(data.values.crossplane.namespace)
+ - name: release
+ base:
+ apiVersion: helm.crossplane.io/v1beta1
+ kind: Release
+ spec:
+ forProvider:
+ namespace: #@ data.values.crossplane.namespace
+ chart:
+ name: postgresql
+ repository: https://charts.bitnami.com/bitnami
+ version: 12.1.2
+ values:
+ architecture: standalone
+ global:
+ postgresql:
+ auth:
+ secretKeys:
+ adminPasswordKey: password
+
+ connectionDetails:
+ - name: type
+ value: postgresql
+ - name: provider
+ value: #@ data.values.providers.helm.name
+ - name: database
+ fromFieldPath: spec.forProvider.values.global.postgresql.auth.database
+ type: FromFieldPath
+ - name: username
+ fromFieldPath: spec.forProvider.values.global.postgresql.auth.username
+ type: FromFieldPath
+ - name: host
+ fromFieldPath: metadata.annotations.address
+ type: FromFieldPath
+ - name: port
+ value: "5432"
+
+ patches:
+ - type: CombineFromComposite
+ toFieldPath: spec.forProvider.values.primary.initdb.args
+ policy:
+ fromFieldPath: Required
+ combine:
+ strategy: string
+ string:
+ fmt: '-E %s --locale=%s'
+ variables:
+ - fromFieldPath: spec.parameters.charset
+ - fromFieldPath: spec.parameters.collation
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.version
+ toFieldPath: spec.forProvider.values.image.tag
+ transforms:
+ - type: map
+ map:
+ "11": "11.18.0"
+ "12": "12.13.0"
+ "13": "13.9.0"
+ "14": "14.6.0"
+ "15": "15.1.0"
+ - type: FromCompositeFieldPath
+ fromFieldPath: metadata.uid
+ toFieldPath: spec.forProvider.values.global.postgresql.auth.existingSecret
+ transforms:
+ - type: string
+ string:
+ type: Format
+ fmt: '%s-postgresql-admin'
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.adminUsername
+ toFieldPath: spec.forProvider.values.global.postgresql.auth.username
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.storageClass
+ toFieldPath: spec.forProvider.values.primary.persistence.storageClass
+ - type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.database
+ toFieldPath: spec.forProvider.values.global.postgresql.auth.database
+ - type: FromCompositeFieldPath
+ fromFieldPath: status.address
+ toFieldPath: metadata.annotations.address
+ - type: ToCompositeFieldPath
+ toFieldPath: status.location
+ fromFieldPath: spec.forProvider.namespace
+ - type: CombineToComposite
+ toFieldPath: status.address
+ policy:
+ fromFieldPath: Required
+ combine:
+ strategy: string
+ string:
+ fmt: '%s-postgresql.%s.svc.cluster.local'
+ variables:
+ - fromFieldPath: metadata.name
+ - fromFieldPath: spec.forProvider.namespace
+ - type: ToCompositeFieldPath
+ fromFieldPath: spec.forProvider.values.image.tag
+ toFieldPath: status.version
\ No newline at end of file
diff --git a/packages/multicloud/crossplane/psql/ytt/schema.ytt.yml b/packages/multicloud/crossplane/psql/ytt/schema.ytt.yml
new file mode 100644
index 0000000..2c07057
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/ytt/schema.ytt.yml
@@ -0,0 +1,48 @@
+#@data/values-schema
+---
+
+xrd:
+ group: multi.ref.services.apps.tanzu.vmware.com
+ names:
+ kind: XPostgreSQLInstance
+ plural: xpostgresqlinstances
+ claimNames:
+ kind: PostgreSQLInstance
+ plural: postgresqlinstances
+ version: v1alpha1
+
+configurationName: "multicloud-psql"
+cloudServiceBindingType: "postgresql"
+
+providers:
+ azure:
+ name: azure
+ image: xpkg.upbound.io/upbound/provider-azure
+ version: ">=v0.18.1"
+ configRef: default
+
+ helm:
+ name: helm
+ image: xpkg.upbound.io/crossplane-contrib/provider-helm
+ version: ">=v0.12.0"
+
+ aws:
+ name: aws
+ image: xpkg.upbound.io/upbound/provider-aws
+ version: ">=v0.22.0"
+ configRef: default
+
+crossplane:
+ #@schema/title "CrossplaneNamespace"
+ #@schema/desc "The namespace where crossplane controller is installed"
+ namespace: upbound-system
+ version: '^v1.10'
+
+#@schema/title "StoreConfig"
+#@schema/desc "Details of the StoreConfig"
+storeConfig:
+ #@schema/title "StoreConfig Name"
+ #@schema/desc "The name of the StoreConfig"
+ name: "default"
+
+version: ""
diff --git a/packages/multicloud/crossplane/psql/ytt/shared.lib.yml b/packages/multicloud/crossplane/psql/ytt/shared.lib.yml
new file mode 100644
index 0000000..1b89072
--- /dev/null
+++ b/packages/multicloud/crossplane/psql/ytt/shared.lib.yml
@@ -0,0 +1,113 @@
+
+#@ def labelsForSecret(infra):
+name: connectionSecret
+base:
+ apiVersion: kubernetes.crossplane.io/v1alpha1
+ kind: Object
+ spec:
+ forProvider:
+ manifest:
+ apiVersion: v1
+ kind: Secret
+ spec: {}
+ metadata:
+ labels:
+ services.apps.tanzu.vmware.com/class: multicloud-psql
+ services.apps.tanzu.vmware.com/infra: #@ infra
+ type: connection.crossplane.io/v1alpha1
+patches:
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.labels[crossplane.io/claim-name]
+ toFieldPath: spec.forProvider.manifest.metadata.name
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.labels[crossplane.io/claim-namespace]
+ toFieldPath: spec.forProvider.manifest.metadata.namespace
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.version
+ toFieldPath: spec.forProvider.manifest.metadata.labels[services.apps.tanzu.vmware.com/version]
+- type: FromCompositeFieldPath
+ fromFieldPath: spec.parameters.location
+ toFieldPath: spec.forProvider.manifest.metadata.labels[services.apps.tanzu.vmware.com/location]
+#@ end
+
+---
+#@ def tfProviderConfig(crossplaneNamespace):
+name: tf-providerconfig
+base:
+ apiVersion: kubernetes.crossplane.io/v1alpha1
+ kind: Object
+ spec:
+ forProvider:
+ manifest:
+ apiVersion: tf.upbound.io/v1beta1
+ kind: ProviderConfig
+ spec:
+ credentials: []
+ #@yaml/text-templated-strings
+ configuration: |
+ terraform {
+ required_providers {
+ random = {
+ source = "hashicorp/random"
+ version = "3.4.3"
+ }
+ }
+
+ backend "kubernetes" {
+ secret_suffix = "providerconfig-default"
+ namespace = "(@= crossplaneNamespace @)"
+ in_cluster_config = true
+ }
+ }
+ provider "random" {}
+patches:
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.uid
+ toFieldPath: spec.forProvider.manifest.metadata.name
+#@ end
+
+---
+#@ def tfWorkspace(crossplaneNamespace):
+name: password
+base:
+ apiVersion: tf.upbound.io/v1beta1
+ kind: Workspace
+ spec:
+ forProvider:
+ module: |
+ resource "random_password" "password" {
+ length = 64
+ special = false
+ }
+
+ output "password" {
+ value = random_password.password.result
+ sensitive = true
+ }
+ source: Inline
+ writeConnectionSecretToRef:
+ namespace: #@ crossplaneNamespace
+patches:
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.uid
+ toFieldPath: spec.writeConnectionSecretToRef.name
+ transforms:
+ - type: string
+ string:
+ type: Format
+ fmt: '%s-postgresql-admin'
+- type: FromCompositeFieldPath
+ fromFieldPath: metadata.uid
+ toFieldPath: spec.providerConfigRef.name
+- type: ToCompositeFieldPath
+ fromFieldPath: metadata.labels[crossplane.io/claim-name]
+ toFieldPath: spec.writeConnectionSecretToRef.name
+- type: ToCompositeFieldPath
+ fromFieldPath: metadata.labels[crossplane.io/claim-namespace]
+ toFieldPath: spec.writeConnectionSecretToRef.namespace
+- type: ToCompositeFieldPath
+ fromFieldPath: metadata.labels[crossplane.io/claim-name]
+ toFieldPath: status.binding.name
+connectionDetails:
+- fromConnectionSecretKey: password
+#@ end
diff --git a/repository/.imgpkg/images.yml b/repository/.imgpkg/images.yml
deleted file mode 100644
index 5eaecdb..0000000
--- a/repository/.imgpkg/images.yml
+++ /dev/null
@@ -1,28 +0,0 @@
----
-apiVersion: imgpkg.carvel.dev/v1alpha1
-images:
-- annotations:
- kbld.carvel.dev/id: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/elasticache.aws.references.services.apps.tanzu.vmware.com@sha256:cd0ed7ad049c51116e57d604c62214344d1f53b18c5a9c189329839a80401caa
- kbld.carvel.dev/origins: |
- - preresolved:
- url: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/elasticache.aws.references.services.apps.tanzu.vmware.com@sha256:cd0ed7ad049c51116e57d604c62214344d1f53b18c5a9c189329839a80401caa
- image: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/elasticache.aws.references.services.apps.tanzu.vmware.com@sha256:cd0ed7ad049c51116e57d604c62214344d1f53b18c5a9c189329839a80401caa
-- annotations:
- kbld.carvel.dev/id: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/psql.aws.references.services.apps.tanzu.vmware.com@sha256:01481af67e9be08c466e7d98eb1e2e0e904a4965d289d793c41d316aeef59332
- kbld.carvel.dev/origins: |
- - preresolved:
- url: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/psql.aws.references.services.apps.tanzu.vmware.com@sha256:01481af67e9be08c466e7d98eb1e2e0e904a4965d289d793c41d316aeef59332
- image: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/psql.aws.references.services.apps.tanzu.vmware.com@sha256:01481af67e9be08c466e7d98eb1e2e0e904a4965d289d793c41d316aeef59332
-- annotations:
- kbld.carvel.dev/id: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/psql.azure.references.services.apps.tanzu.vmware.com@sha256:c8342f48fce1c27d84a51efb923eefee077ea4b457a3df109598363068641dd9
- kbld.carvel.dev/origins: |
- - preresolved:
- url: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/psql.azure.references.services.apps.tanzu.vmware.com@sha256:c8342f48fce1c27d84a51efb923eefee077ea4b457a3df109598363068641dd9
- image: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/psql.azure.references.services.apps.tanzu.vmware.com@sha256:c8342f48fce1c27d84a51efb923eefee077ea4b457a3df109598363068641dd9
-- annotations:
- kbld.carvel.dev/id: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/psql.google.references.services.apps.tanzu.vmware.com@sha256:9fc1a2767adac504e06d6eb8c9b2cbecc887ad605e3652401f26dc101abd1c6d
- kbld.carvel.dev/origins: |
- - preresolved:
- url: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/psql.google.references.services.apps.tanzu.vmware.com@sha256:9fc1a2767adac504e06d6eb8c9b2cbecc887ad605e3652401f26dc101abd1c6d
- image: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/psql.google.references.services.apps.tanzu.vmware.com@sha256:9fc1a2767adac504e06d6eb8c9b2cbecc887ad605e3652401f26dc101abd1c6d
-kind: ImagesLock
diff --git a/repository/packages/amazon/elasticache/metadata.yml b/repository/packages/amazon/elasticache/metadata.yml
deleted file mode 100644
index e2f898b..0000000
--- a/repository/packages/amazon/elasticache/metadata.yml
+++ /dev/null
@@ -1,17 +0,0 @@
----
-apiVersion: data.packaging.carvel.dev/v1alpha1
-kind: PackageMetadata
-metadata:
- creationTimestamp: null
- name: elasticache.aws.references.services.apps.tanzu.vmware.com
-spec:
- categories:
- - services
- displayName: AWS Elasticache instances
- longDescription: |
- Helps you install an AWS Elasticache for Redis using ACK
- maintainers:
- - name: The TAP Reference Packages Team
- providerName: VMware
- shortDescription: elasticache.services.k8s.aws
- supportDescription: Not supported - provided as reference package only
diff --git a/repository/packages/amazon/elasticache/package.yml b/repository/packages/amazon/elasticache/package.yml
deleted file mode 100644
index 16eed5b..0000000
--- a/repository/packages/amazon/elasticache/package.yml
+++ /dev/null
@@ -1,122 +0,0 @@
----
-apiVersion: data.packaging.carvel.dev/v1alpha1
-kind: Package
-metadata:
- annotations:
- kbld.k14s.io/images: |
- - origins:
- - resolved:
- tag: 0.0.1-alpha
- url: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/elasticache.aws.references.services.apps.tanzu.vmware.com:0.0.1-alpha
- url: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/elasticache.aws.references.services.apps.tanzu.vmware.com@sha256:cd0ed7ad049c51116e57d604c62214344d1f53b18c5a9c189329839a80401caa
- creationTimestamp: null
- name: elasticache.aws.references.services.apps.tanzu.vmware.com.0.0.1-alpha
-spec:
- refName: elasticache.aws.references.services.apps.tanzu.vmware.com
- releasedAt: "2022-10-19T09:41:52Z"
- template:
- spec:
- deploy:
- - kapp: {}
- fetch:
- - imgpkgBundle:
- image: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/elasticache.aws.references.services.apps.tanzu.vmware.com@sha256:cd0ed7ad049c51116e57d604c62214344d1f53b18c5a9c189329839a80401caa
- template:
- - ytt:
- paths:
- - config
- - kbld:
- paths:
- - '-'
- - .imgpkg/images.yml
- valuesSchema:
- openAPIv3:
- additionalProperties: false
- properties:
- name:
- title: Name
- type: string
- description: Name of the Elasticache instance.
- default: ""
- namespace:
- title: Namespace
- type: string
- description: Namespace to deploy the kubernetes resources to.
- default: ""
- createNamespace:
- title: CreateNamespace
- type: boolean
- description: Whether to create the namespace for the resources or not.
- default: false
- cacheSubnetGroupName:
- title: CacheSubnetGroupName
- type: string
- description: Name of the cache subnet group to create the replication group in. It must be pre-created if the createCacheSubnetGroup parameter is set to false.
- default: ""
- createCacheSubnetGroup:
- title: CreateCacheSubnetGroup
- type: boolean
- description: Whether to create the CacheSubnetGroup or not.
- default: false
- subnetIDs:
- title: SubnetIDs
- type: array
- description: The list of subnets to create the CacheSubnetGroup for. Mandatory if the createCacheSubnetGroup parameter is set to true.
- items:
- type: string
- default: ""
- default: []
- cacheNodeType:
- title: CacheNodeType
- type: string
- description: Type of nodes used for the replication group.
- default: cache.t2.micro
- vpcSecurityGroupIDs:
- title: VpcSecurityGroupIDs
- type: array
- description: The list of security groups to associate to the replication group.
- items:
- type: string
- default: ""
- default: []
- instanceClassName:
- title: InstanceClassName
- type: string
- description: The name of the instance class we want to use for binding secrets.
- default: aws-elasticache
- serviceInstanceLabels:
- title: Service Instance Labels
- nullable: true
- description: A set of labels which will be applied to the claimable secret.
- default: {}
- engineVersion:
- title: EngineVersion
- type: string
- description: The version of the Redis engine to use. Available versions can be obtained running `aws cdescribe-cache-engine-versions --engine redis`.
- default: "6.2"
- replicasPerNodeGroup:
- title: ReplicasPerNodeGroup
- type: integer
- description: Number of replicas per node group. Allowed values are from 0 to 5.
- default: 0
- tags:
- title: Tags
- type: array
- description: Tags to attach to all the resources.
- items:
- type: object
- additionalProperties: false
- properties:
- key:
- title: Key
- type: string
- description: The name of the tag.
- default: ""
- value:
- title: Value
- type: string
- description: The value of the tag.
- default: ""
- default: []
- type: object
- version: 0.0.1-alpha
diff --git a/repository/packages/amazon/rds/meta.yaml b/repository/packages/amazon/rds/meta.yaml
deleted file mode 100644
index 6c64fac..0000000
--- a/repository/packages/amazon/rds/meta.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-apiVersion: data.packaging.carvel.dev/v1alpha1
-kind: PackageMetadata
-metadata:
- name: psql.aws.references.services.apps.tanzu.vmware.com
-spec:
- categories:
- - services
- displayName: Amazon RDS instances
- longDescription: |
- Install Amazon RDS instances
- maintainers:
- - name: The Services Toolkit team
- providerName: VMware
- shortDescription: rds
- supportDescription: Not supported - provided as reference package only
diff --git a/repository/packages/amazon/rds/package.yaml b/repository/packages/amazon/rds/package.yaml
deleted file mode 100644
index 3902927..0000000
--- a/repository/packages/amazon/rds/package.yaml
+++ /dev/null
@@ -1,57 +0,0 @@
-apiVersion: data.packaging.carvel.dev/v1alpha1
-kind: Package
-metadata:
- name: psql.aws.references.services.apps.tanzu.vmware.com.0.0.1-alpha
-spec:
- refName: psql.aws.references.services.apps.tanzu.vmware.com
- version: 0.0.1-alpha
- releasedAt: "2021-12-07T15:58:50+00:00"
- releaseNotes: https://docs.vmware.com/en/Services-Toolkit-for-VMware-Tanzu/0.7/services-toolkit-0-7/GUID-overview.html
- valuesSchema:
- openAPIv3:
- type: object
- additionalProperties: false
- properties:
- name:
- type: string
- default: ""
- namespace:
- type: string
- default: default
- database:
- type: string
- default: postgres
- vpcSecurityGroupIDs:
- type: array
- items:
- type: string
- default: ""
- default: []
- dbSubnetGroupName:
- type: string
- default: ""
- engine:
- type: object
- additionalProperties:
- allow: false
- properties:
- version:
- type: string
- description: The PostgresVersion to use.
- enum:
- - "14"
- template:
- spec:
- fetch:
- - imgpkgBundle:
- image: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/psql.aws.references.services.apps.tanzu.vmware.com@sha256:01481af67e9be08c466e7d98eb1e2e0e904a4965d289d793c41d316aeef59332
- template:
- - ytt:
- paths:
- - config/
- - kbld:
- paths:
- - "-"
- - ".imgpkg/images.yml"
- deploy:
- - kapp: {}
diff --git a/repository/packages/azure/psql/package.yml b/repository/packages/azure/psql/package.yml
deleted file mode 100644
index 0786b36..0000000
--- a/repository/packages/azure/psql/package.yml
+++ /dev/null
@@ -1,246 +0,0 @@
----
-apiVersion: data.packaging.carvel.dev/v1alpha1
-kind: Package
-metadata:
- creationTimestamp: null
- name: psql.azure.references.services.apps.tanzu.vmware.com.0.0.1-alpha
-spec:
- refName: psql.azure.references.services.apps.tanzu.vmware.com
- releasedAt: "2022-09-26T10:17:49Z"
- template:
- spec:
- deploy:
- - kapp: {}
- fetch:
- - imgpkgBundle:
- image: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/psql.azure.references.services.apps.tanzu.vmware.com@sha256:c8342f48fce1c27d84a51efb923eefee077ea4b457a3df109598363068641dd9
- template:
- - ytt:
- paths:
- - config
- - kbld:
- paths:
- - '-'
- - .imgpkg/images.yml
- valuesSchema:
- openAPIv3:
- additionalProperties: false
- properties:
- aso_controller_namespace:
- default: azureserviceoperator-system
- description: The Namespace where the Azure ASO controller is installed that
- should own this Azure RM resource
- title: ASO Controller Namespace
- type: string
- create_namespace:
- default: false
- description: Whether to create the namespace for the resources or not
- title: Create namespace flag
- type: boolean
- database:
- additionalProperties: false
- description: The database that will be created.
- properties:
- name:
- default: ""
- description: Name of the database
- title: Name
- type: string
- tags:
- default: []
- description: Tags to attach to the object
- items:
- additionalProperties: false
- properties:
- key:
- default: ""
- description: The name of the tag
- title: Key
- type: string
- value:
- default: ""
- description: The value of the tag
- title: Value
- type: string
- type: object
- title: Tags
- type: array
- title: Database
- type: object
- firewall_rules:
- default: []
- description: List of firewall rules for exposing the Flexible Server. '0.0.0.0'
- for both startIpAddress and endIpAddress means it will be available from
- Azure (not the whole public Internet). Must be IPv4 format.
- items:
- additionalProperties: false
- properties:
- endIpAddress:
- default: ""
- description: The ending IP address of the range
- title: EndIpAddress
- type: string
- startIpAddress:
- default: ""
- description: The starting IP address of the range
- title: StartIpAddress
- type: string
- tags:
- default: []
- description: Tags to attach to the object
- items:
- additionalProperties: false
- properties:
- key:
- default: ""
- description: The name of the tag
- title: Key
- type: string
- value:
- default: ""
- description: The value of the tag
- title: Value
- type: string
- type: object
- title: Tags
- type: array
- type: object
- title: FirewallRules
- type: array
- global_tags:
- default: []
- description: Tags to attach to all the resources
- items:
- additionalProperties: false
- properties:
- key:
- default: ""
- description: The name of the tag
- title: Key
- type: string
- value:
- default: ""
- description: The value of the tag
- title: Value
- type: string
- type: object
- title: GlobalTags
- type: array
- location:
- default: ""
- description: Location where the resources will be created
- title: Location
- type: string
- name:
- default: aso-psql
- description: Name for the resources
- title: ResourceName
- type: string
- namespace:
- default: ""
- description: Kubernetes namespace where the Azure resources will be created
- title: ResourceNamespace
- type: string
- resource_group:
- additionalProperties: false
- description: Azure ResourceGroup for the servers/database resources
- properties:
- name:
- default: aso-psql
- description: Azure ResourceGroup name
- title: Name
- type: string
- tags:
- default: []
- description: Tags to attach to the object
- items:
- additionalProperties: false
- properties:
- key:
- default: ""
- description: The name of the tag
- title: Key
- type: string
- value:
- default: ""
- description: The value of the tag
- title: Value
- type: string
- type: object
- title: Tags
- type: array
- use_existing:
- default: false
- description: Whether to use the existing Azure resource group or not
- title: UseExisting
- type: boolean
- title: ResourceGroup
- type: object
- server:
- additionalProperties: false
- description: FlexibleServer instance that will be created
- properties:
- administrator_name:
- default: myadmin
- description: Username for the administrator user. It cannot be 'azure_superuser',
- 'azuresu', 'azure_pg_admin', 'sa', 'admin', 'administrator', 'root',
- 'guest', 'dbmanager', 'loginmanager', 'dbo', 'information_schema',
- 'sys', 'db_accessadmin', 'db_backupoperator', 'db_datareader', 'db_datawriter',
- 'db_ddladmin', 'db_denydatareader', 'db_denydatawriter', 'db_owner',
- 'db_securityadmin', 'public'.
- title: AdministratorName
- type: string
- instance_storage_size_gb:
- default: 128
- description: 'The storage size for the instance in GB (allowed: from
- 32 to 16384)'
- title: InstanceStorageSizeGB
- type: integer
- instance_tier:
- default: GeneralPurpose
- description: 'The tier of the requested instance (allowed: ''Burstable'',
- ''GeneralPurpose'' or ''Memory Optimized'')'
- title: InstanceTier
- type: string
- instance_type:
- default: Standard_D2s_v3
- description: The type of the requested instance (follows the convention
- Standard_{VM name})
- title: InstanceType
- type: string
- name:
- default: ""
- description: Flexible Server name. It must be unique across all Azure
- postgres database instances. Only lowercase letters, numbers and hyphens
- are allowed.
- title: Name
- type: string
- tags:
- default: []
- description: Tags to attach to the object
- items:
- additionalProperties: false
- properties:
- key:
- default: ""
- description: The name of the tag
- title: Key
- type: string
- value:
- default: ""
- description: The value of the tag
- title: Value
- type: string
- type: object
- title: Tags
- type: array
- version:
- default: "13"
- description: PostgreSQL version to deploy (only 11, 12 and 13 are currently
- supported
- title: Version
- type: string
- title: FlexibleServer
- type: object
- type: object
- version: 0.0.1-alpha
diff --git a/repository/packages/google/cloudsql/meta.yaml b/repository/packages/google/cloudsql/meta.yaml
deleted file mode 100644
index 2a90874..0000000
--- a/repository/packages/google/cloudsql/meta.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-apiVersion: data.packaging.carvel.dev/v1alpha1
-kind: PackageMetadata
-metadata:
- name: psql.google.references.services.apps.tanzu.vmware.com
-spec:
- categories:
- - services
- displayName: Google Cloud SQL instance
- longDescription: |
- Install Google Cloud SQL instance
- maintainers:
- - name: The Services Toolkit team
- providerName: VMware
- shortDescription: cloudsql
- supportDescription: Not supported - provided as reference package only
diff --git a/repository/packages/google/cloudsql/package.yaml b/repository/packages/google/cloudsql/package.yaml
deleted file mode 100644
index d006fe2..0000000
--- a/repository/packages/google/cloudsql/package.yaml
+++ /dev/null
@@ -1,101 +0,0 @@
-apiVersion: data.packaging.carvel.dev/v1alpha1
-kind: Package
-metadata:
- name: psql.google.references.services.apps.tanzu.vmware.com.0.0.1-alpha
-spec:
- refName: psql.google.references.services.apps.tanzu.vmware.com
- version: 0.0.1-alpha
- releasedAt: "2022-07-14T14:47:00+02:00"
- releaseNotes: https://docs.vmware.com/en/Services-Toolkit-for-VMware-Tanzu/0.7/services-toolkit-0-7/GUID-overview.html
- valuesSchema:
- openAPIv3:
- type: object
- additionalProperties: false
- properties:
- name:
- title: Service Instance Name
- type: string
- description: The name of the Cloud SQL instance and related objects
- default: ''
- namespace:
- title: Service Instance Namespace
- type: string
- nullable: true
- description: The namespace the service instance objects should be deployed into
- default: null
- version:
- type: string
- description: 'The database version of the Cloud SQL instance
-
- see: https://cloud.google.com/config-connector/docs/reference/resource-docs/sql/sqlinstance#:~:text=Fields-,databaseVersion,-Optional'
- default: POSTGRES_14
- region:
- title: Database Region
- type: string
- description: 'The region this Cloud SQL instance should be deployed into
-
- see: https://cloud.google.com/config-connector/docs/reference/resource-docs/sql/sqlinstance#:~:text=with%2Dobjects/namespaces/-,region,-Optional'
- default: europe-west6
- tier:
- title: Database Machine Type
- type: string
- description: 'The machine type for the Cloud SQL instance
-
- see: https://cloud.google.com/config-connector/docs/reference/resource-docs/sql/sqlinstance#:~:text=from%20your%20configuration.-,settings.tier,-Required'
- default: db-g1-small
- labels:
- title: Labels
- nullable: true
- description: A set of labels which will be applied to all resources related to
- this instance
- x-example-description: Set custom labels on all objects
- example:
- mycorp.io/service-type: gcp-sql
- mycorp.io/owner: me@mycorp.io
- default:
- app.kubernetes.io/component: cloudsql-postgres
- serviceInstanceLabels:
- title: Service Instance Labels
- nullable: true
- description: A set of labels which will be applied to the claimable secret
- default:
- services.apps.tanzu.vmware.com/class: cloudsql-postgres
- allowedNetworks:
- title: Allowed Networks
- type: array
- nullable: true
- description: A list of CIDR ranges allowed to talk to this Cloud SQL instance
- x-example-description: Allow one (named) network & one host
- example:
- - name: my-onprem-net
- value: 11.22.33.44/24
- - value: 8.8.8.8/32
- items:
- type: object
- additionalProperties: false
- properties:
- name:
- type: string
- nullable: true
- description: the name for the authorized/allowed network (optional)
- default: null
- value:
- type: string
- description: the IPv4 CIDR of the authorized/allowed network
- default: ''
- default: null
- template:
- spec:
- fetch:
- - imgpkgBundle:
- image: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-service-packages/psql.google.references.services.apps.tanzu.vmware.com@sha256:9fc1a2767adac504e06d6eb8c9b2cbecc887ad605e3652401f26dc101abd1c6d
- template:
- - ytt:
- paths:
- - config/
- - kbld:
- paths:
- - "-"
- - ".imgpkg/images.yml"
- deploy:
- - kapp: {}
diff --git a/scripts/carvel-aws-install-ack.sh b/scripts/carvel-aws-install-ack.sh
new file mode 100755
index 0000000..d30b164
--- /dev/null
+++ b/scripts/carvel-aws-install-ack.sh
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+[ -z "$SERVICE" ] && ( echo "The SERVICE environment variable must be defined" ; exit 1 )
+
+CREDENTIALS=${CREDENTIALS:-$HOME/.aws/credentials}
+
+helm_install() {
+ echo ">> Install the AWS $SERVICE Controller for Kubernetes"
+
+ aws ecr-public get-login-password --region us-east-1 | \
+ helm registry login --username AWS --password-stdin public.ecr.aws
+
+ helm upgrade --install \
+ ack-$SERVICE-controller \
+ oci://public.ecr.aws/aws-controllers-k8s/$SERVICE-chart \
+ --create-namespace \
+ --namespace $ACK_NAMESPACE \
+ --version=$RELEASE_VERSION \
+ --set=aws.region=$AWS_REGION \
+ $*
+}
+
+SERVICE=$(tr '[:upper:]' '[:lower:]' <<<$SERVICE)
+RELEASE_VERSION=`curl -sL https://api.github.com/repos/aws-controllers-k8s/$SERVICE-controller/releases/latest | jq -r .tag_name`
+ACK_NAMESPACE=${ACK_NAMESPACE:-ack-system}
+AWS_REGION=${AWS_REGION:-eu-central-1}
+
+echo ">> Define ACK authentication"
+# https://aws-controllers-k8s.github.io/community/docs/user-docs/authentication/
+
+if [ ! -z "${AWS_ACCESS_KEY_ID:-}" -a ! -z "${AWS_SECRET_ACCESS_KEY:-}" ]; then
+ echo "WARNING - Using AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN environment variables"
+ helm_install
+
+ kubectl -n ${ACK_NAMESPACE} set env deployments.apps -l app.kubernetes.io/instance=ack-${SERVICE}-controller \
+ AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID" \
+ AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY" \
+ AWS_SESSION_TOKEN="$AWS_SESSION_TOKEN"
+
+elif [ -r "${CREDENTIALS}" ]; then
+ echo "Using shared credentials file"
+ SECRET="aws-credentials"
+ kubectl create namespace $ACK_NAMESPACE || true
+ kubectl -n ${ACK_NAMESPACE} delete secret ${SECRET} &>/dev/null || true
+ kubectl -n ${ACK_NAMESPACE} create secret generic ${SECRET} --from-file credentials=${CREDENTIALS}
+
+ helm_install --set=aws.credentials.secretName=${SECRET}
+
+fi
diff --git a/scripts/carvel-azure-install-aso.sh b/scripts/carvel-azure-install-aso.sh
new file mode 100755
index 0000000..f3f2bed
--- /dev/null
+++ b/scripts/carvel-azure-install-aso.sh
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+CERT_MANAGER_MANIFEST="https://github.com/jetstack/cert-manager/releases/download/v1.8.2/cert-manager.yaml"
+ASO_MANIFEST="https://github.com/Azure/azure-service-operator/releases/download/v2.0.0-beta.3/azureserviceoperator_v2.0.0-beta.3.yaml"
+ASO_NAMESPACE="azureserviceoperator-system"
+
+echo ">> Install certmanager"
+kubectl apply -f ${CERT_MANAGER_MANIFEST}
+kubectl -n cert-manager wait --for=condition=Available=True deployments.apps cert-manager
+kubectl -n cert-manager wait --for=condition=Available=True deployments.apps cert-manager-cainjector
+kubectl -n cert-manager wait --for=condition=Available=True deployments.apps cert-manager-webhook
+kubectl wait --for=condition=Available apiservices.apiregistration.k8s.io v1.cert-manager.io
+kubectl wait --for=condition=Available apiservices.apiregistration.k8s.io v1.acme.cert-manager.io
+
+echo ">> Install Azure Service Operator"
+kubectl create ns ${ASO_NAMESPACE} || true
+cat <> Prepare security group"
+
+JOBNAME="get-public-ip-${NAME}"
+kubectl create job ${JOBNAME} --image curlimages/curl -- curl -sSf https://api.ipify.org
+kubectl wait --for=condition=Complete=True job ${JOBNAME}
+PUBLIC_IP="$(kubectl logs jobs/${JOBNAME})"
+kubectl delete job ${JOBNAME}
+
+VPC_ID=$(aws elasticache describe-cache-subnet-groups --cache-subnet-group-name $CACHE_SUBNET_GROUP_NAME --query 'CacheSubnetGroups[0].VpcId' --output text)
+SG_NAME="elasticache-test-${NAME}"
+
+# make sure the security group does not exist before creating a new one
+{
+ aws ec2 describe-security-groups --filters 'Name="vpc-id",Values="'${VPC_ID}'"' 'Name="group-name",Values="'${SG_NAME}'"' --query 'SecurityGroups[0].GroupId' --output text | xargs aws ec2 delete-security-group --group-id
+} || true
+SG_ID=$(aws ec2 create-security-group --group-name "${SG_NAME}" --description "elasticache security group for testing" --vpc-id $VPC_ID --output text --query GroupId)
+# trap "aws ec2 delete-security-group --group-id $SG_ID" EXIT
+aws ec2 authorize-security-group-ingress --group-id $SG_ID --cidr ${PUBLIC_IP}/32 --protocol tcp --port 6379
+
+
+VALUES=$(mktemp)
+cat <$VALUES
+---
+name: test-${NAME}
+namespace: ${PACKAGE_NAMESPACE}
+cacheSubnetGroupName: ${CACHE_SUBNET_GROUP_NAME}
+cacheNodeType: cache.t2.micro
+vpcSecurityGroupIDs:
+ - ${SG_ID}
+EOF
+
+# install package
+kubectl create namespace ${PACKAGE_NAMESPACE} || true
+
+SA=${PACKAGE_METADATA_NAME}
+export INSTALL_NAME=${PACKAGE_METADATA_NAME}
+
+echo ">> Prepare RBAC"
+ytt -f ./${SCRIPT_FOLDER}/rbac.ytt.yml -v serviceAccount=${SA} -v namespace=${PACKAGE_NAMESPACE} | kubectl apply -f -
+
+echo ">> Install package"
+kctrl package install -n ${PACKAGE_NAMESPACE} -i ${INSTALL_NAME} -p ${PACKAGE_METADATA_NAME} --version ${PACKAGE_VERSION} --values-file ${VALUES} --service-account-name ${SA} --wait=false
+
+timeout --foreground -s TERM $INSTALL_TIMEOUT bash -c '
+INIT_TIMEOUT_SECONDS=${TIMEOUT:-60}
+MAX_TIMEOUT_SECONDS=${TIMEOUT:-300}
+TIMEOUT_SECONDS=${INIT_TIMEOUT_SECONDS}
+while true; do
+ echo ">> Waiting for stack ${NAME} to reconcile for ${TIMEOUT_SECONDS} seconds..."
+ kubectl -n ${PACKAGE_NAMESPACE} wait --for=condition=ReconcileSucceeded --timeout="${TIMEOUT_SECONDS}s" packageinstalls.packaging.carvel.dev ${INSTALL_NAME} && break || true
+
+ # the cloud controller needs to be kicked because it might conflict with kapp-controller for taking ownership of cloud resources
+ kubectl -n ${ACK_NAMESPACE} rollout restart deployments.apps -l app.kubernetes.io/instance=ack-${SERVICE}-controller
+
+ let TEMP_TIMEOUT=${TIMEOUT_SECONDS}*2
+ [[ ${TEMP_TIMEOUT} > ${MAX_TIMEOUT_SECONDS} ]] && TEMP_TIMEOUT=${MAX_TIMEOUT_SECONDS}
+ TIMEOUT_SECONDS=${TEMP_TIMEOUT}
+done
+'
+
+# run test
+SECRET_NAME="${NAME}-bindable"
+# ./${SCRIPT_FOLDER}/test.sh ${SECRET_NAME} ${APP_NAME}
+
+popd
diff --git a/scripts/carvel-e2e-aws-elasticache.skip/cleanup.sh b/scripts/carvel-e2e-aws-elasticache.skip/cleanup.sh
new file mode 100755
index 0000000..0829981
--- /dev/null
+++ b/scripts/carvel-e2e-aws-elasticache.skip/cleanup.sh
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+pushd $(dirname $0)
+
+[ -z "${PACKAGE_METADATA_NAME:-}" ] && echo "Environment variable PACKAGE_METADATA_NAME is not defined" && exit 1
+
+export PACKAGE_NAMESPACE=${PACKAGE_NAMESPACE:-services}
+export APP_NAMESPACE=${APP_NAMESPACE:-services}
+
+INSTALL_NAME=${PACKAGE_METADATA_NAME}
+
+VALUES_SECRET_NAME=$(kubectl -n ${PACKAGE_NAMESPACE} get packageinstalls.packaging.carvel.dev ${INSTALL_NAME} -o jsonpath='{.spec.values[0].secretRef.name}')
+PACKAGE_DATA=$(kubectl -n ${PACKAGE_NAMESPACE} get secrets ${VALUES_SECRET_NAME} -o jsonpath='{.data}')
+PACKAGE_VALUES=$(jq -e '.data."values.yml"' <<<$PACKAGE_DATA || jq -e '.data."values.yaml"' <<<$PACKAGE_DATA)
+
+NAME=$(base64 -d <<<$PACKAGE_VALUES | yq .name)
+APP_NAMESPACE=$(base64 -d <<<$PACKAGE_VALUES | yq .namespace)
+
+APP_NAME=${APP_NAME:-${NAME}}
+TIMEOUT="5m"
+CHECK_INTERVAL="10s"
+
+
+kubectl -n ${APP_NAMESPACE} delete deployments.apps ${APP_NAME} || true
+
+SA=${PACKAGE_METADATA_NAME}
+
+echo ">> Uninstall package"
+kctrl package installed delete -n ${PACKAGE_NAMESPACE} -i ${INSTALL_NAME} --wait-timeout ${TIMEOUT} --wait-check-interval ${CHECK_INTERVAL} -y || {
+ # the default user gets locked into the ACK.Terminal state because it can be deleted BEFORE its group is deleted
+ # and when a resource get to that state there's no way to get out of it
+ # in order to delete the kubernetes resource I need to remove the finalizers
+ kubectl -n ${APP_NAMESPACE} patch users.elasticache.services.k8s.aws ${NAME}-default --type=json --patch '[{"op":"remove","path":"/metadata/finalizers"}]'
+ DEFAULT_USER_ID=$(kubectl -n ${APP_NAMESPACE} get users.elasticache.services.k8s.aws ${NAME}-default -o jsonpath='{.spec.userID}')
+ aws elasticache delete-user --user-id $DEFAULT_USER_ID
+ kubectl -n ${APP_NAMESPACE} delete users.elasticache.services.k8s.aws ${NAME}-default
+}
+
+echo ">> Remove RBAC"
+ytt -f ./rbac.ytt.yml -v serviceAccount=${SA} -v namespace=${PACKAGE_NAMESPACE} | kubectl delete -f -
+
+popd
diff --git a/scripts/carvel-e2e-aws-elasticache.skip/rbac.ytt.yml b/scripts/carvel-e2e-aws-elasticache.skip/rbac.ytt.yml
new file mode 100644
index 0000000..67c2ae8
--- /dev/null
+++ b/scripts/carvel-e2e-aws-elasticache.skip/rbac.ytt.yml
@@ -0,0 +1,50 @@
+#@ load("@ytt:data", "data")
+#@ load("@ytt:overlay", "overlay")
+
+#@ namespace = data.values.namespace if "namespace" in data.values else "services"
+#@ serviceAccountName = data.values.serviceAccount
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: #@ serviceAccountName
+---
+kind: Role
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: #@ serviceAccountName
+rules:
+- apiGroups: ["elasticache.services.k8s.aws"]
+ resources: ["*"]
+ verbs: ["*"]
+- apiGroups: ["secretgen.carvel.dev", "secretgen.k14s.io"]
+ resources: ["secrettemplates","passwords"]
+ verbs: ["*"]
+- apiGroups: [""]
+ resources: ["serviceaccounts","configmaps"]
+ verbs: ["*"]
+- apiGroups: [""]
+ resources: ["namespaces"]
+ verbs: ["get", "list"]
+- apiGroups: ["rbac.authorization.k8s.io"]
+ resources: ["roles","rolebindings"]
+ verbs: ["*"]
+---
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: #@ serviceAccountName + "-binding"
+subjects:
+- kind: ServiceAccount
+ name: #@ serviceAccountName
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: #@ serviceAccountName
+
+
+#@overlay/match by=overlay.all, expects="1+"
+---
+metadata:
+ #@overlay/match missing_ok=True
+ namespace: #@ namespace
diff --git a/scripts/carvel-e2e-azure-psql.sh b/scripts/carvel-e2e-azure-psql.sh
new file mode 100755
index 0000000..45e5b43
--- /dev/null
+++ b/scripts/carvel-e2e-azure-psql.sh
@@ -0,0 +1,104 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+INSTALL_TIMEOUT="20m"
+
+SCRIPT_FOLDER=$(basename $0 .sh)
+
+export NAME="${NAME:-$(dd if=/dev/urandom bs=20 count=1 2>/dev/null | sha1sum | head -c 20)}"
+export PACKAGE_NAMESPACE=${PACKAGE_NAMESPACE:-services}
+export APP_NAMESPACE=${APP_NAMESPACE:-services}
+APP_NAME=${APP_NAME:-${NAME}}
+export ASO_CONTROLLER_NAMESPACE="azureserviceoperator-system"
+
+[ -z "${PACKAGE_METADATA_NAME:-}" ] && echo "Environment variable PACKAGE_METADATA_NAME is not defined" && exit 1
+[ -z "${PACKAGE_VERSION:-}" ] && echo "Environment variable PACKAGE_VERSION is not defined" && exit 1
+
+while true; do
+ case "${1:-}" in
+ --skip-aso-install)
+ SKIP_ASO_INSTALL=1 ; shift ;;
+ *)
+ break ;;
+ esac
+done
+
+pushd $(dirname $0)
+
+if [ -z ${SKIP_ASO_INSTALL:-} ]; then
+ # install ASO and dependencies
+ ./carvel-azure-install-aso.sh
+fi
+
+echo ">> Prepare package values"
+
+LOCATION="${LOCATION:-westeurope}"
+
+JOBNAME="get-public-ip-${NAME}"
+kubectl create job ${JOBNAME} --image curlimages/curl -- curl -sSf https://api.ipify.org
+kubectl wait --for=condition=Complete=True job ${JOBNAME}
+PUBLIC_IP="$(kubectl logs jobs/${JOBNAME})"
+kubectl delete job ${JOBNAME}
+
+VALUES=$(mktemp)
+cat <$VALUES
+---
+name: ${NAME}
+namespace: ${PACKAGE_NAMESPACE}
+location: ${LOCATION}
+aso_controller_namespace: ${ASO_CONTROLLER_NAMESPACE}
+create_namespace: false
+
+server:
+ administrator_name: testadmin
+
+database:
+ name: testdb
+
+firewall_rules:
+ - startIpAddress: 0.0.0.0
+ endIpAddress: 0.0.0.0
+ - startIpAddress: ${PUBLIC_IP}
+ endIpAddress: ${PUBLIC_IP}
+
+resource_group:
+ use_existing: false
+ name: carvel-test-${NAME}
+EOF
+trap "rm ${VALUES}" EXIT
+
+# install package
+kubectl create namespace ${PACKAGE_NAMESPACE} || true
+
+SA=${PACKAGE_METADATA_NAME}
+export INSTALL_NAME=${PACKAGE_METADATA_NAME}
+
+echo ">> Prepare RBAC"
+ytt -f ./${SCRIPT_FOLDER}/rbac.ytt.yml -v serviceAccount=${SA} -v namespace=${PACKAGE_NAMESPACE} | kubectl apply -f -
+
+echo ">> Install package"
+kctrl package install -n ${PACKAGE_NAMESPACE} -i ${INSTALL_NAME} -p ${PACKAGE_METADATA_NAME} --version ${PACKAGE_VERSION} --values-file ${VALUES} --service-account-name ${SA} --wait=false
+
+timeout --foreground -s TERM $INSTALL_TIMEOUT bash -c '
+INIT_TIMEOUT_SECONDS=${TIMEOUT:-60}
+MAX_TIMEOUT_SECONDS=${TIMEOUT:-300}
+TIMEOUT_SECONDS=${INIT_TIMEOUT_SECONDS}
+while true; do
+ echo ">> Waiting for stack ${NAME} to reconcile for ${TIMEOUT_SECONDS} seconds..."
+ kubectl -n ${PACKAGE_NAMESPACE} wait --for=condition=ReconcileSucceeded --timeout="${TIMEOUT_SECONDS}s" packageinstalls.packaging.carvel.dev ${INSTALL_NAME} && break || true
+
+ # the cloud controller needs to be kicked because it might conflict with kapp-controller for taking ownership of cloud resources
+ kubectl -n ${ASO_CONTROLLER_NAMESPACE} rollout restart deployments.apps azureserviceoperator-controller-manager
+
+ let TEMP_TIMEOUT=${TIMEOUT_SECONDS}*2
+ [[ ${TEMP_TIMEOUT} > ${MAX_TIMEOUT_SECONDS} ]] && TEMP_TIMEOUT=${MAX_TIMEOUT_SECONDS}
+ TIMEOUT_SECONDS=${TEMP_TIMEOUT}
+done
+'
+
+# run test
+SECRET_NAME="${NAME}-bindable"
+./${SCRIPT_FOLDER}/test.sh ${SECRET_NAME} ${APP_NAME}
+
+popd
diff --git a/scripts/carvel-e2e-azure-psql/app-overlay.ytt.yml b/scripts/carvel-e2e-azure-psql/app-overlay.ytt.yml
new file mode 100644
index 0000000..dcb05d8
--- /dev/null
+++ b/scripts/carvel-e2e-azure-psql/app-overlay.ytt.yml
@@ -0,0 +1,28 @@
+#@ load("@ytt:overlay", "overlay")
+#@ load("@ytt:data", "data")
+
+#@ name = data.values.name
+
+#@overlay/match by=overlay.all, expects="1+"
+---
+metadata:
+ name: #@ name
+ labels:
+ app.kubernetes.io/name: #@ name
+spec:
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: #@ name
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: #@ name
+ spec:
+ volumes:
+ #@overlay/match by="name"
+ - name: secret-volume
+ projected:
+ sources:
+ #@overlay/match by=overlay.all, expects="1+"
+ - secret:
+ name: #@ data.values.secret
diff --git a/scripts/carvel-e2e-azure-psql/cleanup.sh b/scripts/carvel-e2e-azure-psql/cleanup.sh
new file mode 100755
index 0000000..03f632f
--- /dev/null
+++ b/scripts/carvel-e2e-azure-psql/cleanup.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+pushd $(dirname $0)
+
+[ -z "${PACKAGE_METADATA_NAME:-}" ] && echo "Environment variable PACKAGE_METADATA_NAME is not defined" && exit 1
+
+export PACKAGE_NAMESPACE=${PACKAGE_NAMESPACE:-services}
+export APP_NAMESPACE=${APP_NAMESPACE:-services}
+VALUES=$(kubectl -n ${PACKAGE_NAMESPACE} get packageinstalls.packaging.carvel.dev ${PACKAGE_METADATA_NAME} -o jsonpath='{.spec.values[0].secretRef.name}')
+NAME=$(kubectl -n ${PACKAGE_NAMESPACE} get secrets ${VALUES} -o jsonpath='{.data.values\.yml}' | base64 -d | yq .name)
+APP_NAME=${APP_NAME:-${NAME}}
+TIMEOUT="15m"
+CHECK_INTERVAL="10s"
+
+
+kubectl -n ${APP_NAMESPACE} delete deployments.apps ${APP_NAME} || true
+
+SA=${PACKAGE_METADATA_NAME}
+INSTALL_NAME=${PACKAGE_METADATA_NAME}
+
+echo ">> Uninstall package"
+kctrl package installed delete -n ${PACKAGE_NAMESPACE} -i ${INSTALL_NAME} --wait-timeout ${TIMEOUT} --wait-check-interval ${CHECK_INTERVAL} -y
+
+echo ">> Remove RBAC"
+ytt -f ./rbac.ytt.yml -v serviceAccount=${SA} -v namespace=${PACKAGE_NAMESPACE} | kubectl delete -f -
+
+popd
diff --git a/scripts/carvel-e2e-azure-psql/rbac.ytt.yml b/scripts/carvel-e2e-azure-psql/rbac.ytt.yml
new file mode 100644
index 0000000..22cd66a
--- /dev/null
+++ b/scripts/carvel-e2e-azure-psql/rbac.ytt.yml
@@ -0,0 +1,53 @@
+#@ load("@ytt:data", "data")
+#@ load("@ytt:overlay", "overlay")
+
+#@ namespace = data.values.namespace if "namespace" in data.values else "services"
+#@ serviceAccountName = data.values.serviceAccount
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: #@ serviceAccountName
+---
+kind: Role
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: #@ serviceAccountName
+rules:
+- apiGroups: ["dbforpostgresql.azure.com"]
+ resources: ["flexibleservers","flexibleserversdatabases","flexibleserversfirewallrules"]
+ verbs: ["*"]
+- apiGroups: ["resources.azure.com"]
+ resources: ["resourcegroups"]
+ verbs: ["*"]
+- apiGroups: ["secretgen.carvel.dev", "secretgen.k14s.io"]
+ resources: ["secrettemplates","passwords"]
+ verbs: ["*"]
+- apiGroups: [""]
+ resources: ["serviceaccounts","configmaps"]
+ verbs: ["*"]
+- apiGroups: [""]
+ resources: ["namespaces"]
+ verbs: ["get", "list"]
+- apiGroups: ["rbac.authorization.k8s.io"]
+ resources: ["roles","rolebindings"]
+ verbs: ["*"]
+---
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: #@ serviceAccountName + "-binding"
+subjects:
+- kind: ServiceAccount
+ name: #@ serviceAccountName
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: #@ serviceAccountName
+
+
+#@overlay/match by=overlay.all, expects="1+"
+---
+metadata:
+ #@overlay/match missing_ok=True
+ namespace: #@ namespace
diff --git a/scripts/carvel-e2e-azure-psql/test.sh b/scripts/carvel-e2e-azure-psql/test.sh
new file mode 100755
index 0000000..03fee47
--- /dev/null
+++ b/scripts/carvel-e2e-azure-psql/test.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+pushd $(dirname $0)
+
+SECRET_NAME=$1
+TEST_APP_NAME=${2:-${SECRET_NAME}}
+APP_NAMESPACE=${APP_NAMESPACE:-default}
+
+echo ">> Installing Test Application"
+MANIFEST="https://raw.githubusercontent.com/joostvdg/spring-boot-postgres/main/kubernetes/deployment.yaml"
+curl -sSfL ${MANIFEST} | ytt -f - -f app-overlay.ytt.yml -v name=${TEST_APP_NAME} -v secret=${SECRET_NAME} | kubectl apply -n ${APP_NAMESPACE} -f -
+
+kubectl -n ${APP_NAMESPACE} get deployments.apps
+
+echo ">> Waiting on Test Application: ${TEST_APP_NAME}"
+kubectl -n ${APP_NAMESPACE} get pods
+kubectl -n ${APP_NAMESPACE} wait --for=condition=Ready pods -l app.kubernetes.io/name=${TEST_APP_NAME} --timeout=300s
+
+kubectl -n ${APP_NAMESPACE} describe pods -l app.kubernetes.io/name=${TEST_APP_NAME}
+sleep 10
+
+echo ">> Starting Port Forward"
+kubectl -n ${APP_NAMESPACE} port-forward deployment/${TEST_APP_NAME} 8080 &
+PORT_FORWARD_PID=$!
+trap "echo '>> Killing Port Forward' && kill -9 ${PORT_FORWARD_PID}" EXIT
+
+sleep 10
+
+echo ">> Testing Application"
+curl -sSfL "http://localhost:8080" && echo
+echo -n ">> Writing record: " && curl -sSfL --header "Content-Type: application/json" --request POST --data '{"name":"Piet"}' http://localhost:8080/create && echo
+echo -n ">> Writing record: " && curl -sSfL --header "Content-Type: application/json" --request POST --data '{"name":"Andrea"}' http://localhost:8080/create && echo
+
+HTTP_RESULT=$(curl -sSfL "http://localhost:8080")
+[ $(jq 'map(select(.name == "Piet")) | length'<<<$HTTP_RESULT) -eq 1 ]
+[ $(jq 'map(select(.name == "Andrea")) | length'<<<$HTTP_RESULT) -eq 1 ]
+
+echo "TEST PASSED"
+
+popd
diff --git a/scripts/crossplane-azure-provider-create-secret.sh b/scripts/crossplane-azure-provider-create-secret.sh
deleted file mode 100755
index e73f6c6..0000000
--- a/scripts/crossplane-azure-provider-create-secret.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-SECRET_NAME=$1
-CREDS=$2
-
-echo ">> Create Azure Config Secret - secret name=${SECRET_NAME}"
-kubectl create secret generic "${SECRET_NAME}" -n upbound-system --from-literal=creds=${CREDS} || true
\ No newline at end of file
diff --git a/scripts/crossplane-e2e-azure-mongodb.sh b/scripts/crossplane-e2e-azure-mongodb.sh
new file mode 100755
index 0000000..81a8adf
--- /dev/null
+++ b/scripts/crossplane-e2e-azure-mongodb.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+trap "echo '###ERROR###' ; top -b -1 -n 1" ERR
+
+echo ">> Local Test"
+
+SCRIPT_FOLDER=$(basename $0 .sh)
+
+[ -z "${CROSSPLANE_NAMESPACE:-}" ] && ( echo "The CROSSPLANE_NAMESPACE environment variable must be defined" ; exit 1 )
+
+kubectl create namespace ${CROSSPLANE_NAMESPACE} || true
+
+pushd $(dirname $0)
+
+# install provider as well as its ProviderConfig only if the INSTALL_PROVIDER environment variable is not empty
+[ -z "${INSTALL_PROVIDER:-}" ] || ./crossplane-install-azure-provider.sh
+
+# install the Crossplane configuration
+./${SCRIPT_FOLDER}/install-package.sh
+
+# create the Crossplane claim
+./${SCRIPT_FOLDER}/claim-instance.sh
+
+# deploy application and test
+./${SCRIPT_FOLDER}/test.sh
+
+popd
diff --git a/scripts/crossplane-e2e-azure-mongodb/app-overlay.ytt.yml b/scripts/crossplane-e2e-azure-mongodb/app-overlay.ytt.yml
new file mode 100644
index 0000000..dcb05d8
--- /dev/null
+++ b/scripts/crossplane-e2e-azure-mongodb/app-overlay.ytt.yml
@@ -0,0 +1,28 @@
+#@ load("@ytt:overlay", "overlay")
+#@ load("@ytt:data", "data")
+
+#@ name = data.values.name
+
+#@overlay/match by=overlay.all, expects="1+"
+---
+metadata:
+ name: #@ name
+ labels:
+ app.kubernetes.io/name: #@ name
+spec:
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: #@ name
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: #@ name
+ spec:
+ volumes:
+ #@overlay/match by="name"
+ - name: secret-volume
+ projected:
+ sources:
+ #@overlay/match by=overlay.all, expects="1+"
+ - secret:
+ name: #@ data.values.secret
diff --git a/scripts/mongodb/crossplane-claim-instance.sh b/scripts/crossplane-e2e-azure-mongodb/claim-instance.sh
similarity index 52%
rename from scripts/mongodb/crossplane-claim-instance.sh
rename to scripts/crossplane-e2e-azure-mongodb/claim-instance.sh
index 22ba1a0..b781ec5 100755
--- a/scripts/mongodb/crossplane-claim-instance.sh
+++ b/scripts/crossplane-e2e-azure-mongodb/claim-instance.sh
@@ -1,7 +1,17 @@
-CLAIM_NAME=$1
+#!/usr/bin/env bash
+set -euo pipefail
+
+CLAIM_NAME=${1:-${CLAIM_NAME:-}}
+[ -z "${CLAIM_NAME:-}" ] && ( echo "The CLAIM_NAME environment variable must be defined" ; exit 1 )
+[ -z "${CROSSPLANE_NAMESPACE:-}" ] && ( echo "The CROSSPLANE_NAMESPACE environment variable must be defined" ; exit 1 )
+
+echo
+kubectl get xmongodbinstances.azure.ref.services.apps.tanzu.vmware.com,mongodbinstances.azure.ref.services.apps.tanzu.vmware.com
+
+echo
echo ">> Claiming a MongoDBInstance"
-cat <> Installing Test Application"
-kubectl apply -f https://raw.githubusercontent.com/joostvdg/spring-boot-mongo/main/kubernetes/raw/deployment.yaml
-kubectl get deployment
+kubectl get xmongodbinstance,mongodbinstance
echo ">> Showing Secrets (1)"
-kubectl get secret -n upbound-system
+kubectl get secret -n ${CROSSPLANE_NAMESPACE}
kubectl get secret
echo ">> Waiting for Managed Resources To Get Ready"
-kubectl wait --for=condition=ready mongodbinstances.azure.ref.services.apps.tanzu.vmware.com ${CLAIM_NAME} --timeout=400s
+kubectl wait --for=condition=ready mongodbinstances.azure.ref.services.apps.tanzu.vmware.com ${CLAIM_NAME} --timeout=10m
echo ">> Showing Secrets (2)"
-kubectl get secret -n upbound-system
+kubectl get secret -n ${CROSSPLANE_NAMESPACE}
kubectl get secret
echo ">> Showing Comp and Claim status"
-kubectl get xmongodbinstance,mongodbinstance
\ No newline at end of file
+kubectl get xmongodbinstance,mongodbinstance
diff --git a/scripts/crossplane-e2e-azure-mongodb/cleanup.sh b/scripts/crossplane-e2e-azure-mongodb/cleanup.sh
new file mode 100755
index 0000000..cd9345b
--- /dev/null
+++ b/scripts/crossplane-e2e-azure-mongodb/cleanup.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+[ -z "${CROSSPLANE_NAMESPACE:-}" ] && ( echo "The CROSSPLANE_NAMESPACE environment variable must be defined" ; exit 1 )
+[ -z "${CLAIM_NAME:-}" ] && ( echo "The CLAIM_NAME environment variable must be defined" ; exit 1 )
+[ -z "${CONFIG_NAME:-}" ] && ( echo "The CONFIG_NAME environment variable must be defined" ; exit 1 )
+
+TEST_APP_NAME=${TEST_APP_NAME:-"spring-boot-mongo"}
+PROVIDER_NAME="upbound-provider-azure"
+AZURE_CONFIG_SECRET_NAME="azure-secret"
+
+echo ">> Cleaning Up Resources"
+echo " > WARNING -> This can take a while (~10 minutes), as it waits for Azure to delete the resources!"
+
+kubectl delete mongodbinstance ${CLAIM_NAME} || true
+kubectl delete deploy ${TEST_APP_NAME} || true
+kubectl delete MongoDatabase -l crossplane.io/claim-name=${CLAIM_NAME} || true
+kubectl delete MongoCollection -l crossplane.io/claim-name=${CLAIM_NAME} || true
+kubectl delete Account -l crossplane.io/claim-name=${CLAIM_NAME} || true
+kubectl delete ResourceGroup -l crossplane.io/claim-name=${CLAIM_NAME} || true
+kubectl delete configuration ${CONFIG_NAME} || true
+
+[ -z "${INSTALL_PROVIDER:-}" ] || (
+ kubectl delete providerconfig.azure.upbound.io default || true
+ kubectl delete secret ${AZURE_CONFIG_SECRET_NAME} -n ${CROSSPLANE_NAMESPACE} || true
+ kubectl delete providers.pkg.crossplane.io ${PROVIDER_NAME} || true
+)
diff --git a/scripts/crossplane-e2e-azure-mongodb/install-package.sh b/scripts/crossplane-e2e-azure-mongodb/install-package.sh
new file mode 100755
index 0000000..249f701
--- /dev/null
+++ b/scripts/crossplane-e2e-azure-mongodb/install-package.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+CONFIG_IMAGE=${1:-${CONFIG_IMAGE:-}}
+CONFIG_VERSION=${2:-${CONFIG_VERSION:-}}
+[ -z "${CONFIG_IMAGE:-}" ] && ( echo "The CONFIG_IMAGE environment variable must be defined" ; exit 1 )
+[ -z "${CONFIG_VERSION:-}" ] && ( echo "The CONFIG_VERSION environment variable must be defined" ; exit 1 )
+
+echo ">> Installing Crossplane Package via Configuration CR"
+cat <> Installing Test Application"
+MANIFEST="https://raw.githubusercontent.com/joostvdg/spring-boot-mongo/main/kubernetes/raw/deployment.yaml"
+curl -sSfL ${MANIFEST} | ytt -f - -f app-overlay.ytt.yml -v name=${TEST_APP_NAME} -v secret=${CLAIM_NAME} | kubectl apply -n default -f -
+kubectl get deployment
+
+echo ">> Waiting on Test Application: ${TEST_APP_NAME}"
+kubectl get pod
+kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=${TEST_APP_NAME} --timeout=120s
+
+echo ">> Starting Port Forward"
+kubectl port-forward deployment/${TEST_APP_NAME} 8080 &
+PORT_FORWARD_PID=$!
+
+sleep 10
+
+echo ">> Testing Application"
+curl -sSfL "http://localhost:8080" && echo
+echo -n ">> Writing record: " && curl -sSfL --header "Content-Type: application/json" --request POST --data '{"name":"Piet"}' http://localhost:8080/create && echo
+echo -n ">> Writing record: " && curl -sSfL --header "Content-Type: application/json" --request POST --data '{"name":"Andrea"}' http://localhost:8080/create && echo
+
+HTTP_RESULT=$(curl -sSfL "http://localhost:8080")
+[ $(jq 'map(select(.name == "Piet")) | length'<<<$HTTP_RESULT) -eq 1 ]
+[ $(jq 'map(select(.name == "Andrea")) | length'<<<$HTTP_RESULT) -eq 1 ]
+
+echo "TEST PASSED"
+
+echo ">> Killing Port Forward"
+echo " > PORT_FORWARD_PID=$PORT_FORWARD_PID"
+kill -9 $PORT_FORWARD_PID
+
+popd
diff --git a/scripts/crossplane-e2e-mongodb.sh b/scripts/crossplane-e2e-mongodb.sh
deleted file mode 100755
index 49ddc27..0000000
--- a/scripts/crossplane-e2e-mongodb.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-echo ">> Local Test"
-
-AZURE_CREDS_SECRET_NAME=${AZURE_CREDS_SECRET_NAME:-"azure-secret"}
-UXP_VERSION=${UXP_VERSION:-"v1.10.1-up.1"}
-export CONFIG_NAME=${CONFIG_NAME:-"trp-azure-mongodb"}
-CONFIG_IMAGE=${CONFIG_IMAGE:-"ghcr.io/vmware-tanzu-labs/tap-reference-packages-azure/crossplane-mongodb"}
-CONFIG_VERSION=${CONFIG_VERSION:-"0.23.1-beta.0"}
-export CLAIM_NAME=${CLAIM_NAME:-"trp-cosmosdb-mongo-08"}
-export TEST_APP_NAME=${TEST_APP_NAME:-"spring-boot-mongo"}
-
-kubectl create namespace upbound-system || true
-
-# Requires AZURE_CONFIG to contain an Azure API credential config (JSON format)
-./crossplane-azure-provider-create-secret.sh ${AZURE_CREDS_SECRET_NAME} "${AZURE_CONFIG}"
-
-./crossplane-install-uxp.sh ${UXP_VERSION}
-
-./mongodb/crossplane-install-package.sh ${CONFIG_NAME} ${CONFIG_IMAGE} ${CONFIG_VERSION} ${AZURE_CREDS_SECRET_NAME}
-
-./mongodb/crossplane-claim-instance.sh ${CLAIM_NAME}
-
-./mongodb/crossplane-test.sh ${TEST_APP_NAME}
-
-./mongodb/crossplane-test.sh ${TEST_APP_NAME}
diff --git a/scripts/crossplane-e2e-multicloud-psql.sh b/scripts/crossplane-e2e-multicloud-psql.sh
new file mode 100755
index 0000000..cf4325e
--- /dev/null
+++ b/scripts/crossplane-e2e-multicloud-psql.sh
@@ -0,0 +1,73 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+trap "echo '###ERROR###' ; top -b -1 -n 1" ERR
+
+echo ">> Local Test"
+
+SCRIPT_FOLDER=$(basename $0 .sh)
+
+[ -z "${CROSSPLANE_NAMESPACE:-}" ] && ( echo "The CROSSPLANE_NAMESPACE environment variable must be defined" ; exit 1 )
+
+kubectl create namespace ${CROSSPLANE_NAMESPACE} || true
+
+pushd $(dirname $0)
+
+
+# echo ">> Local Test"
+
+# export CROSSPLANE_NAMESPACE=${CROSSPLANE_NAMESPACE:-upbound-system}
+# AZURE_CREDS_SECRET_NAME=${AZURE_CREDS_SECRET_NAME:-"azure-secret"}
+# UXP_VERSION=${UXP_VERSION:-"v1.10.1-up.1"}
+# CONFIG_NAME=${CONFIG_NAME:-"trp-multicloud-psql"}
+# CONFIG_IMAGE=${CONFIG_IMAGE:-"ghcr.io/vmware-tanzu-labs/trp-azure-psql"}
+# CONFIG_VERSION=${CONFIG_VERSION:-"0.0.1-rc-1"}
+# CLAIM_NAME=${CLAIM_NAME:-"postgresql-0001"}
+# TEST_APP_NAME=${TEST_APP_NAME:-"spring-boot-postgres"}
+export STORAGE_CLASS=${STORAGE_CLASS:-"standard"}
+
+
+# kubectl create namespace ${CROSSPLANE_NAMESPACE} || true
+
+# pushd $(dirname $0)
+
+# TODO use this when we also have the Azure version
+# Requires AZURE_CONFIG to contain an Azure API credential config (JSON format)
+# ./crossplane-azure-provider-create-secret.sh ${AZURE_CREDS_SECRET_NAME} "${AZURE_CONFIG}"
+
+# ./crossplane-install-uxp.sh ${UXP_VERSION}
+
+
+echo "> Installing required providers"
+
+# trap $(dirname $0)/crossplane-e2e-multicloud-psql/cleanup.sh EXIT
+
+# install provider as well as its ProviderConfig only if the INSTALL_PROVIDER environment variable is not empty
+[ -z "${INSTALL_PROVIDER:-}" ] || (
+ ./crossplane-install-helm-provider.sh
+ ./crossplane-install-k8s-provider.sh
+ ./crossplane-install-tf-provider.sh
+)
+
+./${SCRIPT_FOLDER}/install-package.sh ${CONFIG_NAME} ${CONFIG_IMAGE} ${CONFIG_VERSION}
+./${SCRIPT_FOLDER}/claim-helm-instance.sh
+./${SCRIPT_FOLDER}/test.sh
+
+# ./${SCRIPT_FOLDER}/cleanup.sh
+
+# sleep 5
+
+# [ -z "${INSTALL_PROVIDER:-}" ] || (
+# ./crossplane-install-azure-provider.sh
+# ./crossplane-install-k8s-provider.sh
+# ./crossplane-install-tf-provider.sh
+# )
+
+# ./${SCRIPT_FOLDER}/install-package.sh ${CONFIG_NAME} ${CONFIG_IMAGE} ${CONFIG_VERSION}
+# ./${SCRIPT_FOLDER}/claim-azure-instance.sh ${CLAIM_NAME} ${STORAGE_CLASS}
+# ./${SCRIPT_FOLDER}/test.sh
+
+# ./${SCRIPT_FOLDER}/cleanup.sh
+
+popd
diff --git a/scripts/crossplane-e2e-multicloud-psql/app-overlay.ytt.yml b/scripts/crossplane-e2e-multicloud-psql/app-overlay.ytt.yml
new file mode 100644
index 0000000..dcb05d8
--- /dev/null
+++ b/scripts/crossplane-e2e-multicloud-psql/app-overlay.ytt.yml
@@ -0,0 +1,28 @@
+#@ load("@ytt:overlay", "overlay")
+#@ load("@ytt:data", "data")
+
+#@ name = data.values.name
+
+#@overlay/match by=overlay.all, expects="1+"
+---
+metadata:
+ name: #@ name
+ labels:
+ app.kubernetes.io/name: #@ name
+spec:
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: #@ name
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: #@ name
+ spec:
+ volumes:
+ #@overlay/match by="name"
+ - name: secret-volume
+ projected:
+ sources:
+ #@overlay/match by=overlay.all, expects="1+"
+ - secret:
+ name: #@ data.values.secret
diff --git a/scripts/crossplane-e2e-multicloud-psql/claim-azure-instance.sh b/scripts/crossplane-e2e-multicloud-psql/claim-azure-instance.sh
new file mode 100755
index 0000000..e618ec8
--- /dev/null
+++ b/scripts/crossplane-e2e-multicloud-psql/claim-azure-instance.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+
+CLAIM_NAME=$1
+STORAGE_CLASS=$2
+CROSSPLANE_NAMESPACE=${CROSSPLANE_NAMESPACE:-upbound-system}
+
+echo ">> Claiming a PSQl Instance"
+cat <> Showing Secrets (1)"
+kubectl get secret -n ${CROSSPLANE_NAMESPACE}
+kubectl get secret
+
+echo ">> Waiting for Managed Resources To Get Ready"
+kubectl wait --for=condition=ready postgresqlinstances.multi.ref.services.apps.tanzu.vmware.com/${CLAIM_NAME} --timeout=600s
+# We can also wait on the "release"
+
+echo ">> Showing Secrets (2)"
+kubectl get secret -n ${CROSSPLANE_NAMESPACE}
+kubectl get secret
+
+echo ">> Showing Comp and Claim status"
+kubectl get xpostgresqlinstances,postgresqlinstances
diff --git a/scripts/crossplane-e2e-multicloud-psql/claim-helm-instance.sh b/scripts/crossplane-e2e-multicloud-psql/claim-helm-instance.sh
new file mode 100755
index 0000000..36d715e
--- /dev/null
+++ b/scripts/crossplane-e2e-multicloud-psql/claim-helm-instance.sh
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+[ -z "${CLAIM_NAME:-}" ] && ( echo "The CLAIM_NAME environment variable must be defined" ; exit 1 )
+[ -z "${STORAGE_CLASS:-}" ] && ( echo "The STORAGE_CLASS environment variable must be defined" ; exit 1 )
+[ -z "${CROSSPLANE_NAMESPACE:-}" ] && ( echo "The CROSSPLANE_NAMESPACE environment variable must be defined" ; exit 1 )
+
+echo ">> Claiming a PSQL Instance"
+kubectl apply -f - <> Showing Secrets (1)"
+kubectl get secret -n ${CROSSPLANE_NAMESPACE}
+kubectl get secret
+
+kubectl get managed
+trap 'kubectl get managed ; kubectl describe postgresqlinstances.multi.ref.services.apps.tanzu.vmware.com/${CLAIM_NAME} ; kubectl get xpostgresqlinstances -o yaml ; kubectl get secrets ${CLAIM_NAME} -o yaml' ERR
+
+echo ">> Waiting for Managed Resources To Get Ready"
+kubectl wait --for=condition=ready postgresqlinstances.multi.ref.services.apps.tanzu.vmware.com/${CLAIM_NAME} --timeout=120s
+# We can also wait on the "release"
+
+echo ">> Showing Secrets (2)"
+kubectl get secret -n ${CROSSPLANE_NAMESPACE}
+kubectl get secret
+
+echo ">> Showing Comp and Claim status"
+kubectl get xpostgresqlinstances,postgresqlinstances
diff --git a/scripts/crossplane-e2e-multicloud-psql/cleanup.sh b/scripts/crossplane-e2e-multicloud-psql/cleanup.sh
new file mode 100755
index 0000000..650298d
--- /dev/null
+++ b/scripts/crossplane-e2e-multicloud-psql/cleanup.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+CLAIM_NAME=${CLAIM_NAME:-"postgresql-0001"}
+TEST_APP_NAME=${TEST_APP_NAME:-"spring-boot-postgres"}
+CONFIG_NAME=${CONFIG_NAME:-"trp-multicloud-psql"}
+
+echo ">> Cleaning Up Resources"
+echo " > WARNING -> This can take a while (~10 minutes), as it waits for Azure to delete the resources!"
+
+kubectl delete postgresqlinstances ${CLAIM_NAME} || true
+kubectl delete deploy ${TEST_APP_NAME} || true
+kubectl delete configuration ${CONFIG_NAME} || true
+
+kubectl delete flexibleserverconfigurations.dbforpostgresql.azure.upbound.io -l crossplane.io/claim-name=${CLAIM_NAME} --force --grace-period=0 || true
+kubectl delete flexibleserverdatabases.dbforpostgresql.azure.upbound.io -l crossplane.io/claim-name=${CLAIM_NAME} --force --grace-period=0 || true
+kubectl delete flexibleserverfirewallrules.dbforpostgresql.azure.upbound.io -l crossplane.io/claim-name=${CLAIM_NAME} --force --grace-period=0 || true
+
+FLEXIBLE_SERVER_NAME=$(kubectl get flexibleserver.dbforpostgresql.azure.upbound.io -l crossplane.io/claim-name=${CLAIM_NAME} -o name)
+kubectl patch ${FLEXIBLE_SERVER_NAME} -p '{"metadata":{"finalizers":null}}' --type=merge || true
+kubectl delete flexibleserver.dbforpostgresql.azure.upbound.io -l crossplane.io/claim-name=${CLAIM_NAME} --force --grace-period=0 || true
+
+
+kubectl delete providerconfigs.helm.crossplane.io default || true
+kubectl delete providerconfigs.kubernetes.crossplane.io default || true
+
+kubectl delete providers.pkg.crossplane.io crossplane-contrib-provider-helm || true
+kubectl delete providers.pkg.crossplane.io crossplane-contrib-provider-kubernetes || true
+kubectl delete providers.pkg.crossplane.io crossplane-contrib-provider-terraform || true
+
diff --git a/scripts/crossplane-e2e-multicloud-psql/install-package.sh b/scripts/crossplane-e2e-multicloud-psql/install-package.sh
new file mode 100755
index 0000000..fe5983a
--- /dev/null
+++ b/scripts/crossplane-e2e-multicloud-psql/install-package.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+CONFIG_NAME=${1:-${CONFIG_NAME:-}}
+CONFIG_IMAGE=${2:-${CONFIG_IMAGE:-}}
+CONFIG_VERSION=${3:-${CONFIG_VERSION:-}}
+[ -z "${CONFIG_NAME:-}" ] && ( echo "The CONFIG_NAME environment variable must be defined" ; exit 1 )
+[ -z "${CONFIG_IMAGE:-}" ] && ( echo "The CONFIG_IMAGE environment variable must be defined" ; exit 1 )
+[ -z "${CONFIG_VERSION:-}" ] && ( echo "The CONFIG_VERSION environment variable must be defined" ; exit 1 )
+
+echo ">> Installing Crossplane Package via Configuration CR"
+cat <> Installing Test Application"
+MANIFEST="https://raw.githubusercontent.com/joostvdg/spring-boot-postgres/main/kubernetes/deployment.yaml"
+curl -sSfL ${MANIFEST} | ytt -f - -f app-overlay.ytt.yml -v name=${TEST_APP_NAME} -v secret=${CLAIM_NAME} | kubectl apply -n default -f -
+kubectl get deployment
+
+trap 'kubectl get pods -o yaml' ERR
+
+echo ">> Waiting on Test Application: ${TEST_APP_NAME}"
+kubectl get pod
+kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=${TEST_APP_NAME} --timeout=300s
+
+kubectl describe pod -l app.kubernetes.io/name=${TEST_APP_NAME}
+
+sleep 10
+
+echo ">> Starting Port Forward"
+kubectl port-forward deployment/${TEST_APP_NAME} 8080 &
+PORT_FORWARD_PID=$!
+
+sleep 10
+
+echo ">> Testing Application"
+curl -sSfL "http://localhost:8080" && echo
+echo -n ">> Writing record: " && curl -sSfL --header "Content-Type: application/json" --request POST --data '{"name":"Piet"}' http://localhost:8080/create && echo
+echo -n ">> Writing record: " && curl -sSfL --header "Content-Type: application/json" --request POST --data '{"name":"Andrea"}' http://localhost:8080/create && echo
+
+HTTP_RESULT=$(curl -sSfL "http://localhost:8080")
+[ $(jq 'map(select(.name == "Piet")) | length'<<<$HTTP_RESULT) -eq 1 ]
+[ $(jq 'map(select(.name == "Andrea")) | length'<<<$HTTP_RESULT) -eq 1 ]
+
+echo "TEST PASSED"
+
+echo ">> Killing Port Forward"
+echo " > PORT_FORWARD_PID=$PORT_FORWARD_PID"
+kill -9 $PORT_FORWARD_PID
+
+popd
diff --git a/scripts/crossplane-install-azure-provider.sh b/scripts/crossplane-install-azure-provider.sh
new file mode 100755
index 0000000..58c7edf
--- /dev/null
+++ b/scripts/crossplane-install-azure-provider.sh
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+[ -z "${CROSSPLANE_NAMESPACE:-}" ] && ( echo "The CROSSPLANE_NAMESPACE environment variable must be defined" ; exit 1 )
+[ -z "${AZURE_CONFIG:-}" ] && ( echo "The AZURE_CONFIG environment variable must be defined" ; exit 1 )
+
+PROVIDER_NAME="upbound-provider-azure"
+AZURE_CONFIG_SECRET_NAME="azure-secret"
+
+kubectl create namespace ${CROSSPLANE_NAMESPACE} || true
+
+echo ">> Install ${PROVIDER_NAME} provider"
+up controlplane provider install xpkg.upbound.io/upbound/provider-azure:v0.18.1 --name ${PROVIDER_NAME} || true
+
+kubectl wait --for=condition="Healthy" providers.pkg.crossplane.io ${PROVIDER_NAME}
+kubectl wait --for=condition=Available=True apiservices.apiregistration.k8s.io v1beta1.azure.upbound.io
+
+echo ">> Create Azure Config Secret - secret name=${AZURE_CONFIG_SECRET_NAME}"
+kubectl delete secret ${AZURE_CONFIG_SECRET_NAME} -n ${CROSSPLANE_NAMESPACE} || true
+kubectl create secret generic ${AZURE_CONFIG_SECRET_NAME} -n ${CROSSPLANE_NAMESPACE} --from-literal=creds="${AZURE_CONFIG}"
+
+echo ">> Create Azure Provider Config"
+cat <> Installing UXP - Universal Crossplane"
up uxp install --set 'args={--enable-external-secret-stores}' ${UXP_VERSION}
-kubectl wait --for=condition=ready pod -l app.kubernetes.io/component=cloud-infrastructure-controller --namespace upbound-system
\ No newline at end of file
+kubectl wait --for=condition=ready pod -l app.kubernetes.io/component=cloud-infrastructure-controller --namespace ${CROSSPLANE_NAMESPACE}
+
+kubectl create clusterrolebinding crossplane-admin-binding --clusterrole cluster-admin --serviceaccount="upbound-system:crossplane" || true
\ No newline at end of file
diff --git a/scripts/kind-cluster.sh b/scripts/kind-cluster.sh
deleted file mode 100755
index db852ab..0000000
--- a/scripts/kind-cluster.sh
+++ /dev/null
@@ -1 +0,0 @@
-kind create cluster --name test --wait 5m
\ No newline at end of file
diff --git a/scripts/mongodb/crossplane-e2e-cleanup.sh b/scripts/mongodb/crossplane-e2e-cleanup.sh
deleted file mode 100755
index 4cc9dd3..0000000
--- a/scripts/mongodb/crossplane-e2e-cleanup.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-CLAIM_NAME=${CLAIM_NAME:-"trp-cosmosdb-mongo-08"}
-TEST_APP_NAME=${TEST_APP_NAME:-"spring-boot-mongo"}
-CONFIG_NAME=${CONFIG_NAME:-"trp-azure-mongodb"}
-
-echo ">> Cleaning Up Resources"
-echo " > WARNING -> This can take a while (~10 minutes), as it waits for Azure to delete the resources!"
-
-kubectl delete mongodbinstance ${CLAIM_NAME} || true
-kubectl delete deploy ${TEST_APP_NAME} || true
-kubectl delete MongoDatabase -l crossplane.io/claim-name=${CLAIM_NAME} || true
-kubectl delete MongoCollection -l crossplane.io/claim-name=${CLAIM_NAME} || true
-kubectl delete Account -l crossplane.io/claim-name=${CLAIM_NAME} || true
-kubectl delete ResourceGroup -l crossplane.io/claim-name=${CLAIM_NAME} || true
-kubectl delete configuration ${CONFIG_NAME} || true
-kubectl delete providerconfig.azure.upbound.io default || true
diff --git a/scripts/mongodb/crossplane-install-package.sh b/scripts/mongodb/crossplane-install-package.sh
deleted file mode 100755
index 23ab550..0000000
--- a/scripts/mongodb/crossplane-install-package.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-CONFIG_NAME=$1
-CONFIG_IMAGE=$2
-CONFIG_VERSION=$3
-AZURE_CONFIG_SECRET_NAME=$4
-
-echo ">> Installing Crossplane Package via Configuration CR"
-cat <> Create Azure Provider Config"
-cat <> Waiting on Test Application: ${TEST_APP_NAME}"
-kubectl get pod
-kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=${TEST_APP_NAME} --timeout=60s
-
-echo ">> Starting Port Forward"
-kubectl port-forward deployment/${TEST_APP_NAME} 8080 &
-PORT_FORWARD_PID=$!
-
-sleep 10
-
-echo ">> Testing Application"
-curl -s "http://localhost:8080"
-curl --header "Content-Type: application/json" --request POST --data '{"name":"Piet"}' http://localhost:8080/create
-curl --header "Content-Type: application/json" --request POST --data '{"name":"Andrea"}' http://localhost:8080/create
-HTTP_RESULT=$(curl -s "http://localhost:8080")
-[ $(jq 'map(select(.name =="Piet")) | length'<<<$HTTP_RESULT) -eq 1 ]
-[ $(jq 'map(select(.name =="Andrea")) | length'<<<$HTTP_RESULT) -eq 1 ]
-
-echo ">> Killing Port Forward"
-echo " > PORT_FORWARD_PID=$PORT_FORWARD_PID"
-kill -9 $PORT_FORWARD_PID
\ No newline at end of file