├── .dockerignore ├── .github ├── dependabot.yml └── workflows │ ├── cd-image-canary-release.yaml │ ├── cd-operator-release.yaml │ ├── codeql.yml │ ├── integration.yaml │ └── trivy.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── PROJECT ├── README.md ├── api └── v1alpha1 │ ├── atlasmigration_types.go │ ├── atlasschema_types.go │ ├── context.go │ ├── groupversion_info.go │ ├── project_config.go │ ├── reason.go │ ├── target.go │ ├── types_test.go │ └── zz_generated.deepcopy.go ├── charts └── atlas-operator │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ ├── _helpers.tpl │ ├── crds │ │ └── crd.yaml │ ├── deployment.yaml │ ├── leader-election-rbac.yaml │ ├── manager-rbac.yaml │ └── serviceaccount.yaml │ └── values.yaml ├── cmd └── main.go ├── config ├── crd │ ├── bases │ │ ├── db.atlasgo.io_atlasmigrations.yaml │ │ └── db.atlasgo.io_atlasschemas.yaml │ ├── kustomization.yaml │ └── kustomizeconfig.yaml ├── customconfig │ └── kustomization.yaml ├── default │ ├── kustomization.yaml │ ├── manager_auth_proxy_patch.yaml │ └── manager_config_patch.yaml ├── manager │ ├── kustomization.yaml │ └── manager.yaml ├── manifests │ └── kustomization.yaml ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── rbac │ ├── atlasmigration_editor_role.yaml │ ├── atlasmigration_viewer_role.yaml │ ├── atlasschema_editor_role.yaml │ ├── atlasschema_viewer_role.yaml │ ├── auth_proxy_client_clusterrole.yaml │ ├── auth_proxy_role.yaml │ ├── auth_proxy_role_binding.yaml │ ├── auth_proxy_service.yaml │ ├── kustomization.yaml │ ├── leader_election_role.yaml │ ├── leader_election_role_binding.yaml │ ├── role.yaml │ ├── role_binding.yaml │ └── service_account.yaml ├── samples │ ├── atlasmigration │ │ ├── cloud │ │ │ ├── atlas.yaml │ │ │ └── resource.yaml │ │ ├── default │ │ │ ├── dir.yaml │ │ │ └── resource.yaml │ │ └── local │ │ │ └── resource.yaml │ ├── db_v1alpha1_atlasschema.yaml │ └── kustomization.yaml ├── scorecard │ ├── bases │ │ └── config.yaml │ ├── kustomization.yaml │ └── patches │ │ ├── basic.config.yaml │ │ └── olm.config.yaml └── sqlserver │ └── kustomization.yaml ├── go.mod ├── go.sum ├── hack ├── boilerplate.go.txt └── boilerplate.yaml.txt ├── internal ├── controller │ ├── atlasmigration_controller.go │ ├── atlasmigration_controller_test.go │ ├── atlasschema_controller.go │ ├── atlasschema_controller_test.go │ ├── common.go │ ├── common_test.go │ ├── devdb.go │ ├── lint.go │ ├── testhelper.go │ └── watch │ │ ├── watch.go │ │ └── watch_test.go └── vercheck │ ├── notification.tmpl │ ├── vercheck.go │ └── vercheck_test.go ├── skaffold.yaml └── test └── e2e ├── e2e_test.go └── testscript ├── migration-backoff-limit.txtar ├── migration-config-variables.txtar ├── migration-mysql.txtar ├── migration-template-dir.txtar ├── schema-backoff-limit.txtar ├── schema-clickhouse.txtar ├── schema-composite.txtar ├── schema-config-lint-destructive.txtar ├── schema-config-variables.txtar ├── schema-hash-configmap.txtar ├── schema-hash.txtar ├── schema-lint-destructive.txtar ├── schema-manual-changes.txtar ├── schema-mariadb.txtar ├── schema-multi-tenancy.txtar ├── schema-mysql.txtar ├── schema-plan-destructive.txtar ├── schema-plan-no-push.txtar ├── schema-plan-pre-approved.txtar ├── schema-policy-diff.txtar ├── schema-postgres.txtar ├── schema-registry.txtar ├── schema-review-always.txtar └── schema-sqlserver.txtar /.dockerignore: -------------------------------------------------------------------------------- 1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file 2 | # Ignore build and test binaries. 3 | bin/ 4 | testbin/ 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | version: 2 16 | updates: 17 | - package-ecosystem: "gomod" 18 | directory: "/" 19 | schedule: 20 | interval: "daily" 21 | commit-message: 22 | prefix: "[go] " 23 | - package-ecosystem: "docker" 24 | directory: "/" 25 | schedule: 26 | interval: "daily" 27 | commit-message: 28 | prefix: "[docker] " 29 | -------------------------------------------------------------------------------- /.github/workflows/cd-image-canary-release.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: CD - Image Canary Release 16 | on: 17 | schedule: 18 | - cron: '0 7 * * 0-4' 19 | jobs: 20 | push: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Set up Docker Buildx 25 | uses: docker/setup-buildx-action@v2 26 | - name: Set up QEMU 27 | uses: docker/setup-qemu-action@v2 28 | - name: Login to DockerHub 29 | uses: docker/login-action@v1 30 | with: 31 | username: ${{ secrets.DOCKERHUB_USERNAME }} 32 | password: ${{ secrets.DOCKERHUB_TOKEN }} 33 | - name: Fetch Atlas version 34 | id: atlas 35 | run: | 36 | # All distributions are built from the same version 37 | # so we only need to fetch it once. 38 | echo "version=$(curl -s https://release.ariga.io/atlas/atlas-linux-amd64-latest.version)" >> $GITHUB_OUTPUT 39 | - name: Docker meta 40 | id: meta 41 | uses: docker/metadata-action@v4 42 | with: 43 | images: ${{ secrets.DOCKERHUB_USERNAME }}/atlas-operator 44 | tags: | 45 | type=schedule 46 | type=sha 47 | type=ref,event=branch 48 | type=semver,pattern={{version}} 49 | labels: | 50 | io.ariga.atlas.version=${{ steps.atlas.outputs.version }} 51 | org.opencontainers.image.title=atlas-operator 52 | org.opencontainers.image.description=Atlas Operator 53 | org.opencontainers.image.url=https://atlasgo.io 54 | org.opencontainers.image.vendor=Ariga 55 | org.opencontainers.image.source=https://github.com/ariga/atlas-operator/blob/master/Dockerfile 56 | - name: Build and push 57 | uses: docker/build-push-action@v4 58 | with: 59 | context: . 60 | build-args: | 61 | ATLAS_VERSION=${{ steps.atlas.outputs.version }} 62 | OPERATOR_VERSION=v${{ steps.meta.outputs.version }} 63 | file: ./Dockerfile 64 | platforms: linux/amd64,linux/arm64 65 | tags: ${{ steps.meta.outputs.tags }} 66 | labels: ${{ steps.meta.outputs.labels }} 67 | push: true 68 | - name: Run Trivy vulnerability scanner 69 | uses: aquasecurity/trivy-action@7b7aa264d83dc58691451798b4d117d53d21edfe 70 | with: 71 | image-ref: ${{ fromJSON(steps.meta.outputs.json).tags[0] }} 72 | format: 'template' 73 | template: '@/contrib/sarif.tpl' 74 | output: 'trivy-results.sarif' 75 | severity: 'CRITICAL,HIGH,MEDIUM' 76 | - name: Upload Trivy scan results to GitHub Security tab 77 | uses: github/codeql-action/upload-sarif@v2 78 | with: 79 | sarif_file: 'trivy-results.sarif' -------------------------------------------------------------------------------- /.github/workflows/cd-operator-release.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: CD - Operator Release 16 | run-name: Release Operator ${{ github.ref_name }} 17 | on: 18 | push: 19 | tags: 20 | - 'v*' 21 | jobs: 22 | helm-test: 23 | name: Test 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v4 27 | - name: Create k8s Kind Cluster 28 | uses: helm/kind-action@v1 29 | - name: Install Skaffold 30 | run: | 31 | curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.3.1/skaffold-linux-amd64 && \ 32 | sudo install skaffold /usr/local/bin/ 33 | - name: Setup Helm 34 | uses: azure/setup-helm@v4 35 | - name: Run e2e tests with Operator 36 | run: | 37 | make test-e2e \ 38 | ATLAS_TOKEN=${{ secrets.ATLAS_TOKEN }} \ 39 | KIND_CLUSTER=chart-testing \ 40 | TEST_RUN='(schema|migration)-mysql' 41 | env: 42 | SKAFFOLD_PROFILE: helm 43 | - name: test env vars 44 | run: | 45 | helm template atlas-operator charts/atlas-operator \ 46 | --set-json=extraEnvs='[{"name":"NORMAL_ENV","value":"value"}]' | grep NORMAL_ENV 47 | docker-build: 48 | name: Build 49 | runs-on: ubuntu-latest 50 | steps: 51 | - uses: actions/checkout@v2 52 | - name: Set up Docker Buildx 53 | uses: docker/setup-buildx-action@v2 54 | - name: Set up QEMU 55 | uses: docker/setup-qemu-action@v2 56 | - name: Login to DockerHub 57 | uses: docker/login-action@v1 58 | with: 59 | username: ${{ secrets.DOCKERHUB_USERNAME }} 60 | password: ${{ secrets.DOCKERHUB_TOKEN }} 61 | - name: Fetch Atlas version 62 | id: atlas 63 | run: | 64 | # All distributions are built from the same version 65 | # so we only need to fetch it once. 66 | echo "version=$(curl -s https://release.ariga.io/atlas/atlas-linux-amd64-latest.version)" >> $GITHUB_OUTPUT 67 | - name: Docker meta 68 | id: meta 69 | uses: docker/metadata-action@v4 70 | with: 71 | images: ${{ secrets.DOCKERHUB_USERNAME }}/atlas-operator 72 | tags: | 73 | type=semver,pattern={{version}},value=${{ github.ref_name }} 74 | labels: | 75 | io.ariga.atlas.version=${{ steps.atlas.outputs.version }} 76 | org.opencontainers.image.title=atlas-operator 77 | org.opencontainers.image.description=Atlas Operator 78 | org.opencontainers.image.url=https://atlasgo.io 79 | org.opencontainers.image.vendor=Ariga 80 | org.opencontainers.image.source=https://github.com/ariga/atlas-operator/blob/master/Dockerfile 81 | - name: Build and push 82 | uses: docker/build-push-action@v4 83 | with: 84 | context: . 85 | build-args: | 86 | ATLAS_VERSION=${{ steps.atlas.outputs.version }} 87 | OPERATOR_VERSION=${{ github.ref_name }} 88 | file: ./Dockerfile 89 | platforms: linux/amd64,linux/arm64 90 | tags: ${{ steps.meta.outputs.tags }} 91 | labels: ${{ steps.meta.outputs.labels }} 92 | push: true 93 | - name: Run Trivy vulnerability scanner 94 | uses: aquasecurity/trivy-action@7b7aa264d83dc58691451798b4d117d53d21edfe 95 | with: 96 | image-ref: ${{ fromJSON(steps.meta.outputs.json).tags[0] }} 97 | format: 'template' 98 | template: '@/contrib/sarif.tpl' 99 | output: 'trivy-results.sarif' 100 | severity: 'CRITICAL,HIGH,MEDIUM' 101 | - name: Upload Trivy scan results to GitHub Security tab 102 | uses: github/codeql-action/upload-sarif@v2 103 | with: 104 | sarif_file: 'trivy-results.sarif' 105 | helm-push: 106 | name: Push to ghcr.io 107 | needs: [docker-build, helm-test] 108 | runs-on: ubuntu-latest 109 | defaults: 110 | run: 111 | working-directory: charts/ 112 | steps: 113 | - uses: actions/checkout@v4 114 | - uses: azure/setup-helm@v4 115 | - name: Validate semver syntax 116 | run: echo "${{ github.ref_name }}" | grep -e "^v[[:digit:]]\{1,3\}.[[:digit:]]\{1,3\}.[[:digit:]]\{1,3\}$" 117 | - name: Get Semantic Version 118 | id: semver 119 | run: | 120 | VERSION=$(echo ${{ github.ref_name }} | sed 's/^v//') 121 | echo "::set-output name=version::$VERSION" 122 | - name: Helm package 123 | run: helm package atlas-operator 124 | - name: login to gcr using helm 125 | run: | 126 | echo ${{ secrets.GITHUB_TOKEN }} | helm registry login ghcr.io/ariga/atlas-operator --username ${{ github.repository_owner }} --password-stdin 127 | - name: helm push 128 | run: | 129 | helm push atlas-operator-${{ steps.semver.outputs.version }}.tgz oci://ghcr.io/ariga/charts 130 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # For most projects, this workflow file will not need changing; you simply need 16 | # to commit it to your repository. 17 | # 18 | # You may wish to alter this file to override the set of languages analyzed, 19 | # or to provide custom queries or build logic. 20 | # 21 | # ******** NOTE ******** 22 | # We have attempted to detect the languages in your repository. Please check 23 | # the `language` matrix defined below to confirm you have the correct set of 24 | # supported CodeQL languages. 25 | # 26 | name: "CodeQL Advanced" 27 | 28 | on: 29 | push: 30 | branches: [ "master" ] 31 | pull_request: 32 | branches: [ "master" ] 33 | schedule: 34 | - cron: '19 15 * * 0' 35 | 36 | jobs: 37 | analyze: 38 | name: Analyze (${{ matrix.language }}) 39 | # Runner size impacts CodeQL analysis time. To learn more, please see: 40 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 41 | # - https://gh.io/supported-runners-and-hardware-resources 42 | # - https://gh.io/using-larger-runners (GitHub.com only) 43 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 44 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 45 | permissions: 46 | # required for all workflows 47 | security-events: write 48 | 49 | # required to fetch internal or private CodeQL packs 50 | packages: read 51 | 52 | # only required for workflows in private repositories 53 | actions: read 54 | contents: read 55 | 56 | strategy: 57 | fail-fast: false 58 | matrix: 59 | include: 60 | - language: go 61 | build-mode: autobuild 62 | # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' 63 | # Use `c-cpp` to analyze code written in C, C++ or both 64 | # Use 'java-kotlin' to analyze code written in Java, Kotlin or both 65 | # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 66 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 67 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 68 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 69 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 70 | steps: 71 | - name: Checkout repository 72 | uses: actions/checkout@v4 73 | 74 | # Initializes the CodeQL tools for scanning. 75 | - name: Initialize CodeQL 76 | uses: github/codeql-action/init@v3 77 | with: 78 | languages: ${{ matrix.language }} 79 | build-mode: ${{ matrix.build-mode }} 80 | # If you wish to specify custom queries, you can do so here or in a config file. 81 | # By default, queries listed here will override any specified in a config file. 82 | # Prefix the list here with "+" to use these queries and those in the config file. 83 | 84 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 85 | # queries: security-extended,security-and-quality 86 | 87 | # If the analyze step fails for one of the languages you are analyzing with 88 | # "We were unable to automatically build your code", modify the matrix above 89 | # to set the build mode to "manual" for that language. Then modify this step 90 | # to build your code. 91 | # ℹ️ Command-line programs to run using the OS shell. 92 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 93 | - if: matrix.build-mode == 'manual' 94 | shell: bash 95 | run: | 96 | echo 'If you are using a "manual" build mode for one or more of the' \ 97 | 'languages you are analyzing, replace this with the commands to build' \ 98 | 'your code, for example:' 99 | echo ' make bootstrap' 100 | echo ' make release' 101 | exit 1 102 | 103 | - name: Perform CodeQL Analysis 104 | uses: github/codeql-action/analyze@v3 105 | with: 106 | category: "/language:${{matrix.language}}" 107 | -------------------------------------------------------------------------------- /.github/workflows/integration.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Integration Tests 16 | on: 17 | push: 18 | branches: 19 | - master 20 | pull_request: 21 | jobs: 22 | unit: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v4 26 | - uses: actions/setup-go@v5 27 | with: 28 | go-version-file: 'go.mod' 29 | - name: Install Atlas CLI 30 | uses: ariga/setup-atlas@master 31 | - name: Generate 32 | run: | 33 | make cli-gen 34 | - name: Run Go mod tidy 35 | run: go mod tidy 36 | - name: Verify generated files are checked in properly 37 | run: | 38 | status=$(git status --porcelain) 39 | if [ -n "$status" ]; then 40 | echo "you need to run 'make cli-gen' and commit the changes" 41 | echo "$status" 42 | exit 1 43 | fi 44 | - name: Run tests 45 | run: | 46 | make test 47 | e2e: 48 | runs-on: ubuntu-latest 49 | needs: [unit] 50 | steps: 51 | - uses: actions/checkout@v4 52 | - uses: actions/setup-go@v5 53 | with: 54 | go-version-file: 'go.mod' 55 | - name: Create k8s Kind Cluster 56 | uses: helm/kind-action@v1 57 | - name: Install Skaffold 58 | run: | 59 | curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.3.1/skaffold-linux-amd64 && \ 60 | sudo install skaffold /usr/local/bin/ 61 | - name: Run e2e tests 62 | run: | 63 | make test-e2e \ 64 | ATLAS_TOKEN=${{ secrets.ATLAS_TOKEN }} \ 65 | KIND_CLUSTER=chart-testing 66 | -------------------------------------------------------------------------------- /.github/workflows/trivy.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Trivy 16 | on: 17 | schedule: 18 | - cron: '44 21 * * 4' 19 | workflow_dispatch: 20 | permissions: 21 | contents: read 22 | jobs: 23 | build: 24 | permissions: 25 | contents: read # for actions/checkout to fetch code 26 | security-events: write # for github/codeql-action/upload-sarif to upload SARIF results 27 | actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status 28 | name: Build 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout code 32 | uses: actions/checkout@v3 33 | - name: Build an image from Dockerfile 34 | run: | 35 | docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/atlas-operator:${{ github.sha }} . 36 | - name: Run Trivy vulnerability scanner 37 | uses: aquasecurity/trivy-action@7b7aa264d83dc58691451798b4d117d53d21edfe 38 | with: 39 | image-ref: '${{ secrets.DOCKERHUB_USERNAME }}/atlas-operator:${{ github.sha }}' 40 | format: 'template' 41 | template: '@/contrib/sarif.tpl' 42 | output: 'trivy-results.sarif' 43 | severity: 'CRITICAL,HIGH,MEDIUM' 44 | - name: Upload Trivy scan results to GitHub Security tab 45 | uses: github/codeql-action/upload-sarif@v2 46 | with: 47 | sarif_file: 'trivy-results.sarif' 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Binaries for programs and plugins 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | bin 9 | testbin/* 10 | Dockerfile.cross 11 | 12 | # Test binary, build with `go test -c` 13 | *.test 14 | 15 | # Output of the go coverage tool, specifically when used with LiteIDE 16 | *.out 17 | 18 | # Kubernetes Generated files - skip generated files, except for vendored files 19 | 20 | !vendor/**/zz_generated.* 21 | # editor and IDE paraphernalia 22 | .idea 23 | *.swp 24 | *.swo 25 | *~ 26 | 27 | .kpt-pipeline 28 | .env.secret 29 | .env* -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax = docker/dockerfile:1.4.1 2 | # Copyright 2023 The Atlas Operator Authors. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # Build the manager binary 17 | FROM golang:1.23-alpine3.20 as builder 18 | ARG TARGETOS 19 | ARG TARGETARCH 20 | ARG OPERATOR_VERSION 21 | 22 | WORKDIR /workspace 23 | # Copy the Go Modules manifests 24 | COPY go.mod go.mod 25 | COPY go.sum go.sum 26 | # cache deps before building and copying source so that we don't need to re-download as much 27 | # and so that source changes don't invalidate our downloaded layer 28 | RUN --mount=type=cache,target=/go/pkg/mod \ 29 | --mount=type=cache,target=/root/.cache/go-build \ 30 | go mod download 31 | 32 | # Copy the go source 33 | COPY cmd/main.go cmd/main.go 34 | COPY api/ api/ 35 | COPY internal/ internal/ 36 | 37 | RUN --mount=type=cache,target=/go/pkg/mod \ 38 | --mount=type=cache,target=/root/.cache/go-build \ 39 | GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} CGO_ENABLED=0 \ 40 | go build -ldflags "-X 'main.version=${OPERATOR_VERSION}'" \ 41 | -o manager -a cmd/main.go 42 | 43 | FROM alpine:3.20 as atlas 44 | RUN apk add --no-cache curl 45 | ARG ATLAS_VERSION=latest 46 | ENV ATLAS_VERSION=${ATLAS_VERSION} 47 | RUN curl -sSf https://atlasgo.sh | sh 48 | 49 | FROM alpine:3.20 50 | WORKDIR / 51 | COPY --from=builder /workspace/manager . 52 | COPY --from=atlas /usr/local/bin/atlas /usr/local/bin 53 | RUN chmod +x /usr/local/bin/atlas 54 | ENV ATLAS_KUBERNETES_OPERATOR=1 55 | USER 65532:65532 56 | ENTRYPOINT ["/manager"] 57 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | # Code generated by tool. DO NOT EDIT. 2 | # This file is used to track the info used to scaffold your project 3 | # and allow the plugins properly work. 4 | # More info: https://book.kubebuilder.io/reference/project-config.html 5 | version: "3" 6 | domain: atlasgo.io 7 | layout: 8 | - go.kubebuilder.io/v4 9 | plugins: 10 | manifests.sdk.operatorframework.io/v2: {} 11 | scorecard.sdk.operatorframework.io/v2: {} 12 | projectName: atlas-operator 13 | repo: github.com/ariga/atlas-operator 14 | resources: 15 | - kind: AtlasSchema 16 | domain: atlasgo.io 17 | group: db 18 | version: v1alpha1 19 | api: 20 | crdVersion: v1 21 | namespaced: true 22 | controller: true 23 | path: github.com/ariga/atlas-operator/api/v1alpha1 24 | - kind: AtlasMigration 25 | domain: atlasgo.io 26 | group: db 27 | version: v1alpha1 28 | api: 29 | crdVersion: v1 30 | namespaced: true 31 | controller: true 32 | path: github.com/ariga/atlas-operator/api/v1alpha1 33 | -------------------------------------------------------------------------------- /api/v1alpha1/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Atlas Operator Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package v1alpha1 16 | 17 | import "context" 18 | 19 | type versionCtxKey struct{} 20 | 21 | // WithVersionContext returns a new context with the given verison. 22 | func WithVersionContext(ctx context.Context, version string) context.Context { 23 | return context.WithValue(ctx, versionCtxKey{}, version) 24 | } 25 | 26 | // VersionFromContext returns the version from the given context. 27 | func VersionFromContext(ctx context.Context) string { 28 | if v := ctx.Value(versionCtxKey{}); v != nil { 29 | return v.(string) 30 | } 31 | return "" 32 | } 33 | -------------------------------------------------------------------------------- /api/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Atlas Operator Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package v1alpha1 contains API Schema definitions for the db v1alpha1 API group 16 | // +kubebuilder:object:generate=true 17 | // +groupName=db.atlasgo.io 18 | package v1alpha1 19 | 20 | import ( 21 | "k8s.io/apimachinery/pkg/runtime/schema" 22 | "sigs.k8s.io/controller-runtime/pkg/scheme" 23 | ) 24 | 25 | var ( 26 | // GroupVersion is group version used to register these objects 27 | GroupVersion = schema.GroupVersion{Group: "db.atlasgo.io", Version: "v1alpha1"} 28 | 29 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 30 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 31 | 32 | // AddToScheme adds the types in this group-version to the given scheme. 33 | AddToScheme = SchemeBuilder.AddToScheme 34 | ) 35 | -------------------------------------------------------------------------------- /api/v1alpha1/project_config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Atlas Operator Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package v1alpha1 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | 21 | "ariga.io/atlas-go-sdk/atlasexec" 22 | "github.com/hashicorp/hcl/v2" 23 | "github.com/hashicorp/hcl/v2/hclwrite" 24 | corev1 "k8s.io/api/core/v1" 25 | "sigs.k8s.io/controller-runtime/pkg/client" 26 | ) 27 | 28 | type ( 29 | // ProjectConfigSpec defines the project configuration. 30 | ProjectConfigSpec struct { 31 | // Config defines the project configuration. 32 | // Should be a valid YAML string. 33 | Config string `json:"config,omitempty"` 34 | // ConfigFrom defines the reference to the secret key that contains the project configuration. 35 | ConfigFrom Secret `json:"configFrom,omitempty"` 36 | // EnvName defines the environment name that defined in the project configuration. 37 | // If not defined, the default environment "k8s" will be used. 38 | EnvName string `json:"envName,omitempty"` 39 | // Vars defines the input variables for the project configuration. 40 | Vars []Variable `json:"vars,omitempty"` 41 | } 42 | // Variables defines the reference of secret/configmap to the input variables for the project configuration. 43 | Variable struct { 44 | Key string `json:"key,omitempty"` 45 | Value string `json:"value,omitempty"` 46 | ValueFrom ValueFrom `json:"valueFrom,omitempty"` 47 | } 48 | // ValueFrom defines the reference to the secret key that contains the value. 49 | ValueFrom struct { 50 | // SecretKeyRef defines the secret key reference to use for the value. 51 | SecretKeyRef *corev1.SecretKeySelector `json:"secretKeyRef,omitempty"` 52 | // ConfigMapKeyRef defines the configmap key reference to use for the value. 53 | ConfigMapKeyRef *corev1.ConfigMapKeySelector `json:"configMapKeyRef,omitempty"` 54 | } 55 | ) 56 | 57 | // GetConfig returns the project configuration. 58 | // The configuration is resolved from the secret reference. 59 | func (s ProjectConfigSpec) GetConfig(ctx context.Context, r client.Reader, ns string) (*hclwrite.File, error) { 60 | rawConfig := s.Config 61 | if s.ConfigFrom.SecretKeyRef != nil { 62 | cfgFromSecret, err := getSecretValue(ctx, r, ns, s.ConfigFrom.SecretKeyRef) 63 | if err != nil { 64 | return nil, err 65 | } 66 | rawConfig = cfgFromSecret 67 | } 68 | if rawConfig == "" { 69 | return nil, nil 70 | } 71 | config, diags := hclwrite.ParseConfig([]byte(rawConfig), "", hcl.InitialPos) 72 | if diags.HasErrors() { 73 | return nil, fmt.Errorf("failed to parse project configuration: %v", diags) 74 | } 75 | return config, nil 76 | } 77 | 78 | // GetVars returns the input variables for the project configuration. 79 | // The variables are resolved from the secret or configmap reference. 80 | func (s ProjectConfigSpec) GetVars(ctx context.Context, r client.Reader, ns string) (atlasexec.Vars2, error) { 81 | vars := atlasexec.Vars2{} 82 | for _, variable := range s.Vars { 83 | var ( 84 | value string 85 | err error 86 | ) 87 | value = variable.Value 88 | if variable.ValueFrom.SecretKeyRef != nil { 89 | if value, err = getSecretValue(ctx, r, ns, variable.ValueFrom.SecretKeyRef); err != nil { 90 | return nil, err 91 | } 92 | } 93 | if variable.ValueFrom.ConfigMapKeyRef != nil { 94 | if value, err = getConfigMapValue(ctx, r, ns, variable.ValueFrom.ConfigMapKeyRef); err != nil { 95 | return nil, err 96 | } 97 | } 98 | // Resolve variables with the same key by grouping them into a slice. 99 | // It's necessary when generating Atlas command for list(string) input type. 100 | if existingValue, exists := vars[variable.Key]; exists { 101 | if _, ok := existingValue.([]string); ok { 102 | vars[variable.Key] = append(existingValue.([]string), value) 103 | } else if _, ok := existingValue.(string); ok { 104 | vars[variable.Key] = []string{existingValue.(string), value} 105 | } else { 106 | return nil, fmt.Errorf("invalid variable type for %q", variable.Key) 107 | } 108 | } 109 | vars[variable.Key] = value 110 | } 111 | return vars, nil 112 | } 113 | -------------------------------------------------------------------------------- /api/v1alpha1/reason.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Atlas Operator Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package v1alpha1 16 | 17 | const ( 18 | // ReasonReconciling represents for the reconciliation is in progress. 19 | ReasonReconciling = "Reconciling" 20 | // ReasonGettingDevDB represents the reason for getting the dev database. 21 | ReasonGettingDevDB = "GettingDevDB" 22 | // ReasonWhoAmI represents the reason for getting the current user via Atlas CLI 23 | ReasonWhoAmI = "WhoAmI" 24 | // ReasonApplyingMigration represents the reason for applied a schema/migration resource successfully. 25 | ReasonApplied = "Applied" 26 | // ReasonApprovalPending represents the reason for the approval is pending. 27 | ReasonApprovalPending = "ApprovalPending" 28 | // ReasonCreatingAtlasClient represents the reason for creating an Atlas client. 29 | ReasonCreatingAtlasClient = "CreatingAtlasClient" 30 | // ReasonCreatingWorkingDir represents the reason for creating a working directory. 31 | ReasonCreatingWorkingDir = "CreatingWorkingDir" 32 | ) 33 | 34 | // isFailedReason returns true if the given reason is a failed reason. 35 | func isFailedReason(reason string) bool { 36 | switch reason { 37 | case ReasonReconciling, ReasonGettingDevDB, ReasonApprovalPending: 38 | return false 39 | default: 40 | return true 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /charts/atlas-operator/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/atlas-operator/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: atlas-operator 3 | description: The Atlas Kubernetes Operator 4 | type: application 5 | version: 0.7.8 6 | appVersion: 0.7.8 7 | -------------------------------------------------------------------------------- /charts/atlas-operator/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "atlas-operator.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "atlas-operator.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "atlas-operator.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Common labels 35 | */}} 36 | {{- define "atlas-operator.labels" -}} 37 | helm.sh/chart: {{ include "atlas-operator.chart" . }} 38 | {{ include "atlas-operator.selectorLabels" . }} 39 | {{- if .Chart.AppVersion }} 40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 41 | {{- end }} 42 | app.kubernetes.io/managed-by: {{ .Release.Service }} 43 | {{- end }} 44 | 45 | {{/* 46 | Selector labels 47 | */}} 48 | {{- define "atlas-operator.selectorLabels" -}} 49 | app.kubernetes.io/name: {{ include "atlas-operator.name" . }} 50 | app.kubernetes.io/instance: {{ .Release.Name }} 51 | {{- end }} 52 | 53 | {{/* 54 | Create the name of the service account to use 55 | */}} 56 | {{- define "atlas-operator.serviceAccountName" -}} 57 | {{- if .Values.serviceAccount.create }} 58 | {{- default (include "atlas-operator.fullname" .) .Values.serviceAccount.name }} 59 | {{- else }} 60 | {{- default "default" .Values.serviceAccount.name }} 61 | {{- end }} 62 | {{- end }} 63 | 64 | {{- define "atlas-operator.managerRoleName" -}} 65 | {{ include "atlas-operator.fullname" . }}-manager-role 66 | {{- end }} 67 | 68 | {{- define "atlas-operator.leaderElectionRole" -}} 69 | {{ include "atlas-operator.fullname" . }}-leader-election-role 70 | {{- end }} -------------------------------------------------------------------------------- /charts/atlas-operator/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "atlas-operator.fullname" . }} 5 | labels: 6 | app.kubernetes.io/component: manager 7 | app.kubernetes.io/created-by: atlas-operator 8 | app.kubernetes.io/part-of: atlas-operator 9 | control-plane: controller-manager 10 | {{- include "atlas-operator.labels" . | nindent 4 }} 11 | spec: 12 | replicas: {{ .Values.replicaCount }} 13 | selector: 14 | matchLabels: 15 | control-plane: controller-manager 16 | {{- include "atlas-operator.selectorLabels" . | nindent 6 }} 17 | template: 18 | metadata: 19 | labels: 20 | control-plane: controller-manager 21 | {{- include "atlas-operator.selectorLabels" . | nindent 8 }} 22 | {{- with .Values.podLabels }} 23 | {{- toYaml . | nindent 8 }} 24 | {{- end }} 25 | {{- with .Values.podAnnotations }} 26 | annotations: 27 | {{- toYaml . | nindent 8 }} 28 | kubectl.kubernetes.io/default-container: manager 29 | {{- end }} 30 | spec: 31 | {{- with .Values.extraVolumes }} 32 | volumes: 33 | {{- toYaml . | nindent 6 }} 34 | {{- end }} 35 | containers: 36 | - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 37 | imagePullPolicy: {{ .Values.image.pullPolicy }} 38 | livenessProbe: 39 | httpGet: 40 | path: /healthz 41 | port: 8081 42 | initialDelaySeconds: 15 43 | periodSeconds: 20 44 | name: manager 45 | readinessProbe: 46 | httpGet: 47 | path: /readyz 48 | port: 8081 49 | initialDelaySeconds: 5 50 | periodSeconds: 10 51 | resources: 52 | {{- toYaml .Values.resources | nindent 10 }} 53 | securityContext: 54 | {{- toYaml .Values.containerSecurityContext | nindent 10 }} 55 | env: 56 | - name: PREWARM_DEVDB 57 | value: "{{ .Values.prewarmDevDB }}" 58 | - name: ALLOW_CUSTOM_CONFIG 59 | value: "{{ .Values.allowCustomConfig }}" 60 | {{- with .Values.extraEnvs }} 61 | {{- toYaml . | nindent 8 }} 62 | {{- end }} 63 | {{- with .Values.extraVolumeMounts }} 64 | volumeMounts: 65 | {{- toYaml . | nindent 8 }} 66 | {{- end }} 67 | {{- with .Values.imagePullSecrets }} 68 | imagePullSecrets: 69 | {{- toYaml . | nindent 8 }} 70 | {{- end }} 71 | securityContext: 72 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 73 | serviceAccountName: {{ include "atlas-operator.serviceAccountName" . }} 74 | terminationGracePeriodSeconds: 10 75 | {{- with .Values.nodeSelector }} 76 | nodeSelector: 77 | {{- toYaml . | nindent 8 }} 78 | {{- end }} 79 | {{- with .Values.affinity }} 80 | affinity: 81 | {{- toYaml . | nindent 8 }} 82 | {{- end }} 83 | {{- with .Values.tolerations }} 84 | tolerations: 85 | {{- toYaml . | nindent 8 }} 86 | {{- end }} 87 | -------------------------------------------------------------------------------- /charts/atlas-operator/templates/leader-election-rbac.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.rbac.create -}} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: {{ include "atlas-operator.leaderElectionRole" . }} 6 | labels: 7 | app.kubernetes.io/component: rbac 8 | app.kubernetes.io/created-by: atlas-operator 9 | app.kubernetes.io/part-of: atlas-operator 10 | {{- include "atlas-operator.labels" . | nindent 4 }} 11 | rules: 12 | - apiGroups: 13 | - "" 14 | resources: 15 | - configmaps 16 | verbs: 17 | - get 18 | - list 19 | - watch 20 | - create 21 | - update 22 | - patch 23 | - delete 24 | - apiGroups: 25 | - coordination.k8s.io 26 | resources: 27 | - leases 28 | verbs: 29 | - get 30 | - list 31 | - watch 32 | - create 33 | - update 34 | - patch 35 | - delete 36 | - apiGroups: 37 | - "" 38 | resources: 39 | - events 40 | verbs: 41 | - create 42 | - patch 43 | --- 44 | apiVersion: rbac.authorization.k8s.io/v1 45 | kind: RoleBinding 46 | metadata: 47 | name: {{ include "atlas-operator.leaderElectionRole" . }}-binding 48 | labels: 49 | app.kubernetes.io/component: rbac 50 | app.kubernetes.io/created-by: atlas-operator 51 | app.kubernetes.io/part-of: atlas-operator 52 | {{- include "atlas-operator.labels" . | nindent 4 }} 53 | roleRef: 54 | apiGroup: rbac.authorization.k8s.io 55 | kind: Role 56 | name: '{{ include "atlas-operator.leaderElectionRole" . }}' 57 | subjects: 58 | - kind: ServiceAccount 59 | name: '{{ include "atlas-operator.serviceAccountName" . }}' 60 | namespace: '{{ .Release.Namespace }}' 61 | {{- end }} 62 | -------------------------------------------------------------------------------- /charts/atlas-operator/templates/manager-rbac.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.rbac.create -}} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: {{ include "atlas-operator.managerRoleName" . }} 6 | labels: 7 | {{- include "atlas-operator.labels" . | nindent 4 }} 8 | rules: 9 | - apiGroups: 10 | - apps 11 | resources: 12 | - deployments 13 | verbs: 14 | - create 15 | - delete 16 | - get 17 | - list 18 | - patch 19 | - update 20 | - watch 21 | - apiGroups: 22 | - "" 23 | resources: 24 | - configmaps 25 | - secrets 26 | verbs: 27 | - create 28 | - delete 29 | - get 30 | - list 31 | - update 32 | - watch 33 | - apiGroups: 34 | - "" 35 | resources: 36 | - events 37 | verbs: 38 | - create 39 | - patch 40 | - apiGroups: 41 | - "" 42 | resources: 43 | - pods 44 | verbs: 45 | - create 46 | - delete 47 | - get 48 | - list 49 | - watch 50 | - apiGroups: 51 | - db.atlasgo.io 52 | resources: 53 | - atlasmigrations 54 | verbs: 55 | - create 56 | - delete 57 | - get 58 | - list 59 | - patch 60 | - update 61 | - watch 62 | - apiGroups: 63 | - db.atlasgo.io 64 | resources: 65 | - atlasmigrations/finalizers 66 | verbs: 67 | - update 68 | - apiGroups: 69 | - db.atlasgo.io 70 | resources: 71 | - atlasmigrations/status 72 | verbs: 73 | - get 74 | - patch 75 | - update 76 | - apiGroups: 77 | - db.atlasgo.io 78 | resources: 79 | - atlasschemas 80 | verbs: 81 | - create 82 | - delete 83 | - get 84 | - list 85 | - patch 86 | - update 87 | - watch 88 | - apiGroups: 89 | - db.atlasgo.io 90 | resources: 91 | - atlasschemas/finalizers 92 | verbs: 93 | - update 94 | - apiGroups: 95 | - db.atlasgo.io 96 | resources: 97 | - atlasschemas/status 98 | verbs: 99 | - get 100 | - patch 101 | - update 102 | --- 103 | apiVersion: rbac.authorization.k8s.io/v1 104 | kind: ClusterRoleBinding 105 | metadata: 106 | name: {{ include "atlas-operator.managerRoleName" . }}-binding 107 | labels: 108 | app.kubernetes.io/component: rbac 109 | app.kubernetes.io/created-by: atlas-operator 110 | app.kubernetes.io/part-of: atlas-operator 111 | {{- include "atlas-operator.labels" . | nindent 4 }} 112 | roleRef: 113 | apiGroup: rbac.authorization.k8s.io 114 | kind: ClusterRole 115 | name: '{{ include "atlas-operator.managerRoleName" . }}' 116 | subjects: 117 | - kind: ServiceAccount 118 | name: '{{ include "atlas-operator.serviceAccountName" . }}' 119 | namespace: '{{ .Release.Namespace }}' 120 | {{- end }} 121 | -------------------------------------------------------------------------------- /charts/atlas-operator/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "atlas-operator.serviceAccountName" . }} 6 | labels: 7 | app.kubernetes.io/component: rbac 8 | app.kubernetes.io/created-by: atlas-operator 9 | app.kubernetes.io/part-of: atlas-operator 10 | {{- with .Values.serviceAccount.labels }} 11 | {{- toYaml . | nindent 4 }} 12 | {{- end }} 13 | {{- include "atlas-operator.labels" . | nindent 4 }} 14 | {{- with .Values.serviceAccount.annotations }} 15 | annotations: 16 | {{- toYaml . | nindent 4 }} 17 | {{- end }} 18 | {{- end }} 19 | -------------------------------------------------------------------------------- /charts/atlas-operator/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for atlas-operator. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: arigaio/atlas-operator 9 | pullPolicy: IfNotPresent 10 | tag: "" 11 | 12 | rbac: 13 | create: true 14 | 15 | imagePullSecrets: [] 16 | nameOverride: "" 17 | fullnameOverride: "" 18 | 19 | serviceAccount: 20 | create: true 21 | annotations: {} 22 | labels: {} 23 | name: "" 24 | 25 | podAnnotations: {} 26 | 27 | podLabels: {} 28 | 29 | podSecurityContext: 30 | runAsNonRoot: true 31 | 32 | containerSecurityContext: 33 | runAsUser: 1000 34 | allowPrivilegeEscalation: false 35 | capabilities: 36 | drop: 37 | - ALL 38 | 39 | resources: {} 40 | 41 | nodeSelector: {} 42 | 43 | tolerations: [] 44 | 45 | affinity: {} 46 | 47 | # By default, the operator will recreate devdb pods after migration 48 | # Set this to true to keep the devdb pods around. 49 | prewarmDevDB: true 50 | 51 | # Enable this to allow custom project configuration 52 | # Warning: This setting enables users to use the `external` and `sql` data sources 53 | # which can be used to execute arbitrary commands and queries. Use with caution. 54 | allowCustomConfig: false 55 | 56 | # -- Additional environment variables to set 57 | extraEnvs: [] 58 | # extraEnvs: 59 | # - name: FOO 60 | # valueFrom: 61 | # secretKeyRef: 62 | # key: FOO 63 | # name: secret-resource 64 | # - name: BAR 65 | # valueFrom: 66 | # configMapKeyRef: 67 | # key: BAR 68 | # name: config-map-resource 69 | 70 | extraVolumes: [] 71 | # extraVolumes: 72 | # - name: extra-volume 73 | # secret: 74 | # secretName: extra-volume-secret 75 | 76 | extraVolumeMounts: [] 77 | # extraVolumeMounts: 78 | # - name: extra-volume 79 | # mountPath: /extra-volume 80 | # readOnly: true 81 | 82 | -------------------------------------------------------------------------------- /config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # This kustomization.yaml is not intended to be run by itself, 16 | # since it depends on service name and namespace that are out of this kustomize package. 17 | # It should be run by config/default 18 | resources: 19 | - bases/db.atlasgo.io_atlasschemas.yaml 20 | - bases/db.atlasgo.io_atlasmigrations.yaml 21 | #+kubebuilder:scaffold:crdkustomizeresource 22 | 23 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. 24 | # patches here are for enabling the conversion webhook for each CRD 25 | #- patches/webhook_in_atlasschemas.yaml 26 | #- patches/webhook_in_atlasmigrations.yaml 27 | #+kubebuilder:scaffold:crdkustomizewebhookpatch 28 | 29 | # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. 30 | # patches here are for enabling the CA injection for each CRD 31 | #- patches/cainjection_in_atlasschemas.yaml 32 | #- patches/cainjection_in_atlasmigrations.yaml 33 | #+kubebuilder:scaffold:crdkustomizecainjectionpatch 34 | 35 | # the following config is for teaching kustomize how to do kustomization for CRDs. 36 | configurations: 37 | - kustomizeconfig.yaml 38 | apiVersion: kustomize.config.k8s.io/v1beta1 39 | kind: Kustomization 40 | -------------------------------------------------------------------------------- /config/crd/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD 16 | nameReference: 17 | - kind: Service 18 | version: v1 19 | fieldSpecs: 20 | - kind: CustomResourceDefinition 21 | version: v1 22 | group: apiextensions.k8s.io 23 | path: spec/conversion/webhook/clientConfig/service/name 24 | 25 | namespace: 26 | - kind: CustomResourceDefinition 27 | version: v1 28 | group: apiextensions.k8s.io 29 | path: spec/conversion/webhook/clientConfig/service/namespace 30 | create: false 31 | 32 | varReference: 33 | - path: metadata/annotations 34 | -------------------------------------------------------------------------------- /config/customconfig/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | namespace: atlas-operator-system 16 | apiVersion: kustomize.config.k8s.io/v1beta1 17 | kind: Kustomization 18 | resources: 19 | - ../sqlserver 20 | patches: 21 | - target: 22 | kind: Deployment 23 | namespace: system 24 | name: controller-manager 25 | patch: |- 26 | - op: add 27 | path: "/spec/template/spec/containers/0/env/-" 28 | value: 29 | name: ALLOW_CUSTOM_CONFIG 30 | value: "true" 31 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Adds namespace to all resources. 16 | namespace: atlas-operator-system 17 | 18 | # Value of this field is prepended to the 19 | # names of all resources, e.g. a deployment named 20 | # "wordpress" becomes "alices-wordpress". 21 | # Note that it should also match with the prefix (text before '-') of the namespace 22 | # field above. 23 | namePrefix: atlas-operator- 24 | 25 | # Labels to add to all resources and selectors. 26 | #commonLabels: 27 | # someName: someValue 28 | 29 | apiVersion: kustomize.config.k8s.io/v1beta1 30 | kind: Kustomization 31 | resources: 32 | - ../crd 33 | - ../rbac 34 | - ../manager 35 | -------------------------------------------------------------------------------- /config/default/manager_auth_proxy_patch.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # This patch inject a sidecar container which is a HTTP proxy for the 16 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 17 | apiVersion: apps/v1 18 | kind: Deployment 19 | metadata: 20 | name: controller-manager 21 | namespace: system 22 | spec: 23 | template: 24 | spec: 25 | affinity: 26 | nodeAffinity: 27 | requiredDuringSchedulingIgnoredDuringExecution: 28 | nodeSelectorTerms: 29 | - matchExpressions: 30 | - key: kubernetes.io/arch 31 | operator: In 32 | values: 33 | - amd64 34 | - arm64 35 | - ppc64le 36 | - s390x 37 | - key: kubernetes.io/os 38 | operator: In 39 | values: 40 | - linux 41 | containers: 42 | - name: kube-rbac-proxy 43 | securityContext: 44 | allowPrivilegeEscalation: false 45 | capabilities: 46 | drop: 47 | - "ALL" 48 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.1 49 | args: 50 | - "--secure-listen-address=0.0.0.0:8443" 51 | - "--upstream=http://127.0.0.1:8080/" 52 | - "--logtostderr=true" 53 | - "--v=0" 54 | ports: 55 | - containerPort: 8443 56 | protocol: TCP 57 | name: https 58 | resources: 59 | limits: 60 | cpu: 500m 61 | memory: 128Mi 62 | requests: 63 | cpu: 5m 64 | memory: 64Mi 65 | - name: manager 66 | args: 67 | - "--health-probe-bind-address=:8081" 68 | - "--metrics-bind-address=127.0.0.1:8080" 69 | - "--leader-elect" 70 | -------------------------------------------------------------------------------- /config/default/manager_config_patch.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: controller-manager 19 | namespace: system 20 | spec: 21 | template: 22 | spec: 23 | containers: 24 | - name: manager 25 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | resources: 16 | - manager.yaml 17 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: Namespace 17 | metadata: 18 | labels: 19 | control-plane: controller-manager 20 | app.kubernetes.io/name: namespace 21 | app.kubernetes.io/instance: system 22 | app.kubernetes.io/component: manager 23 | app.kubernetes.io/created-by: atlas-operator 24 | app.kubernetes.io/part-of: atlas-operator 25 | app.kubernetes.io/managed-by: kustomize 26 | name: system 27 | --- 28 | apiVersion: apps/v1 29 | kind: Deployment 30 | metadata: 31 | name: controller-manager 32 | namespace: system 33 | labels: 34 | control-plane: controller-manager 35 | app.kubernetes.io/name: deployment 36 | app.kubernetes.io/instance: controller-manager 37 | app.kubernetes.io/component: manager 38 | app.kubernetes.io/created-by: atlas-operator 39 | app.kubernetes.io/part-of: atlas-operator 40 | app.kubernetes.io/managed-by: kustomize 41 | spec: 42 | selector: 43 | matchLabels: 44 | control-plane: controller-manager 45 | replicas: 1 46 | template: 47 | metadata: 48 | annotations: 49 | kubectl.kubernetes.io/default-container: manager 50 | labels: 51 | control-plane: controller-manager 52 | spec: 53 | # TODO(user): Uncomment the following code to configure the nodeAffinity expression 54 | # according to the platforms which are supported by your solution. 55 | # It is considered best practice to support multiple architectures. You can 56 | # build your manager image using the makefile target docker-buildx. 57 | # affinity: 58 | # nodeAffinity: 59 | # requiredDuringSchedulingIgnoredDuringExecution: 60 | # nodeSelectorTerms: 61 | # - matchExpressions: 62 | # - key: kubernetes.io/arch 63 | # operator: In 64 | # values: 65 | # - amd64 66 | # - arm64 67 | # - ppc64le 68 | # - s390x 69 | # - key: kubernetes.io/os 70 | # operator: In 71 | # values: 72 | # - linux 73 | securityContext: 74 | runAsNonRoot: true 75 | # TODO(user): For common cases that do not require escalating privileges 76 | # it is recommended to ensure that all your Pods/Containers are restrictive. 77 | # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted 78 | # Please uncomment the following code if your project does NOT have to work on old Kubernetes 79 | # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). 80 | # seccompProfile: 81 | # type: RuntimeDefault 82 | containers: 83 | - command: 84 | - /manager 85 | args: 86 | - --leader-elect 87 | image: controller:latest 88 | name: manager 89 | env: [] 90 | securityContext: 91 | runAsUser: 1000 92 | allowPrivilegeEscalation: false 93 | capabilities: 94 | drop: 95 | - "ALL" 96 | livenessProbe: 97 | httpGet: 98 | path: /healthz 99 | port: 8081 100 | initialDelaySeconds: 15 101 | periodSeconds: 20 102 | readinessProbe: 103 | httpGet: 104 | path: /readyz 105 | port: 8081 106 | initialDelaySeconds: 5 107 | periodSeconds: 10 108 | # TODO(user): Configure the resources accordingly based on the project requirements. 109 | # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ 110 | resources: 111 | limits: 112 | cpu: 500m 113 | memory: 128Mi 114 | requests: 115 | cpu: 10m 116 | memory: 64Mi 117 | serviceAccountName: controller-manager 118 | terminationGracePeriodSeconds: 10 119 | -------------------------------------------------------------------------------- /config/manifests/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # These resources constitute the fully configured set of manifests 16 | # used to generate the 'manifests/' directory in a bundle. 17 | resources: 18 | - ../default 19 | - ../samples 20 | - ../scorecard 21 | apiVersion: kustomize.config.k8s.io/v1beta1 22 | kind: Kustomization 23 | -------------------------------------------------------------------------------- /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | resources: 16 | - monitor.yaml 17 | apiVersion: kustomize.config.k8s.io/v1beta1 18 | kind: Kustomization 19 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Prometheus Monitor Service (Metrics) 17 | apiVersion: monitoring.coreos.com/v1 18 | kind: ServiceMonitor 19 | metadata: 20 | labels: 21 | control-plane: controller-manager 22 | app.kubernetes.io/name: servicemonitor 23 | app.kubernetes.io/instance: controller-manager-metrics-monitor 24 | app.kubernetes.io/component: metrics 25 | app.kubernetes.io/created-by: atlas-operator 26 | app.kubernetes.io/part-of: atlas-operator 27 | app.kubernetes.io/managed-by: kustomize 28 | name: controller-manager-metrics-monitor 29 | namespace: system 30 | spec: 31 | endpoints: 32 | - path: /metrics 33 | port: https 34 | scheme: https 35 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 36 | tlsConfig: 37 | insecureSkipVerify: true 38 | selector: 39 | matchLabels: 40 | control-plane: controller-manager 41 | -------------------------------------------------------------------------------- /config/rbac/atlasmigration_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # permissions for end users to edit atlasmigrations. 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: ClusterRole 18 | metadata: 19 | labels: 20 | app.kubernetes.io/name: clusterrole 21 | app.kubernetes.io/instance: atlasmigration-editor-role 22 | app.kubernetes.io/component: rbac 23 | app.kubernetes.io/created-by: atlas-operator 24 | app.kubernetes.io/part-of: atlas-operator 25 | app.kubernetes.io/managed-by: kustomize 26 | name: atlasmigration-editor-role 27 | rules: 28 | - apiGroups: 29 | - db.atlasgo.io 30 | resources: 31 | - atlasmigrations 32 | verbs: 33 | - create 34 | - delete 35 | - get 36 | - list 37 | - patch 38 | - update 39 | - watch 40 | - apiGroups: 41 | - db.atlasgo.io 42 | resources: 43 | - atlasmigrations/status 44 | verbs: 45 | - get 46 | -------------------------------------------------------------------------------- /config/rbac/atlasmigration_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # permissions for end users to view atlasmigrations. 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: ClusterRole 18 | metadata: 19 | labels: 20 | app.kubernetes.io/name: clusterrole 21 | app.kubernetes.io/instance: atlasmigration-viewer-role 22 | app.kubernetes.io/component: rbac 23 | app.kubernetes.io/created-by: atlas-operator 24 | app.kubernetes.io/part-of: atlas-operator 25 | app.kubernetes.io/managed-by: kustomize 26 | name: atlasmigration-viewer-role 27 | rules: 28 | - apiGroups: 29 | - db.atlasgo.io 30 | resources: 31 | - atlasmigrations 32 | verbs: 33 | - get 34 | - list 35 | - watch 36 | - apiGroups: 37 | - db.atlasgo.io 38 | resources: 39 | - atlasmigrations/status 40 | verbs: 41 | - get 42 | -------------------------------------------------------------------------------- /config/rbac/atlasschema_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # permissions for end users to edit atlasschemas. 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: ClusterRole 18 | metadata: 19 | labels: 20 | app.kubernetes.io/name: clusterrole 21 | app.kubernetes.io/instance: atlasschema-editor-role 22 | app.kubernetes.io/component: rbac 23 | app.kubernetes.io/created-by: atlas-operator 24 | app.kubernetes.io/part-of: atlas-operator 25 | app.kubernetes.io/managed-by: kustomize 26 | name: atlasschema-editor-role 27 | rules: 28 | - apiGroups: 29 | - db.atlasgo.io 30 | resources: 31 | - atlasschemas 32 | verbs: 33 | - create 34 | - delete 35 | - get 36 | - list 37 | - patch 38 | - update 39 | - watch 40 | - apiGroups: 41 | - db.atlasgo.io 42 | resources: 43 | - atlasschemas/status 44 | verbs: 45 | - get 46 | -------------------------------------------------------------------------------- /config/rbac/atlasschema_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # permissions for end users to view atlasschemas. 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: ClusterRole 18 | metadata: 19 | labels: 20 | app.kubernetes.io/name: clusterrole 21 | app.kubernetes.io/instance: atlasschema-viewer-role 22 | app.kubernetes.io/component: rbac 23 | app.kubernetes.io/created-by: atlas-operator 24 | app.kubernetes.io/part-of: atlas-operator 25 | app.kubernetes.io/managed-by: kustomize 26 | name: atlasschema-viewer-role 27 | rules: 28 | - apiGroups: 29 | - db.atlasgo.io 30 | resources: 31 | - atlasschemas 32 | verbs: 33 | - get 34 | - list 35 | - watch 36 | - apiGroups: 37 | - db.atlasgo.io 38 | resources: 39 | - atlasschemas/status 40 | verbs: 41 | - get 42 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_client_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: ClusterRole 17 | metadata: 18 | labels: 19 | app.kubernetes.io/name: clusterrole 20 | app.kubernetes.io/instance: metrics-reader 21 | app.kubernetes.io/component: kube-rbac-proxy 22 | app.kubernetes.io/created-by: atlas-operator 23 | app.kubernetes.io/part-of: atlas-operator 24 | app.kubernetes.io/managed-by: kustomize 25 | name: metrics-reader 26 | rules: 27 | - nonResourceURLs: 28 | - "/metrics" 29 | verbs: 30 | - get 31 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: ClusterRole 17 | metadata: 18 | labels: 19 | app.kubernetes.io/name: clusterrole 20 | app.kubernetes.io/instance: proxy-role 21 | app.kubernetes.io/component: kube-rbac-proxy 22 | app.kubernetes.io/created-by: atlas-operator 23 | app.kubernetes.io/part-of: atlas-operator 24 | app.kubernetes.io/managed-by: kustomize 25 | name: proxy-role 26 | rules: 27 | - apiGroups: 28 | - authentication.k8s.io 29 | resources: 30 | - tokenreviews 31 | verbs: 32 | - create 33 | - apiGroups: 34 | - authorization.k8s.io 35 | resources: 36 | - subjectaccessreviews 37 | verbs: 38 | - create 39 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: ClusterRoleBinding 17 | metadata: 18 | labels: 19 | app.kubernetes.io/name: clusterrolebinding 20 | app.kubernetes.io/instance: proxy-rolebinding 21 | app.kubernetes.io/component: kube-rbac-proxy 22 | app.kubernetes.io/created-by: atlas-operator 23 | app.kubernetes.io/part-of: atlas-operator 24 | app.kubernetes.io/managed-by: kustomize 25 | name: proxy-rolebinding 26 | roleRef: 27 | apiGroup: rbac.authorization.k8s.io 28 | kind: ClusterRole 29 | name: proxy-role 30 | subjects: 31 | - kind: ServiceAccount 32 | name: controller-manager 33 | namespace: system 34 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: Service 17 | metadata: 18 | labels: 19 | control-plane: controller-manager 20 | app.kubernetes.io/name: service 21 | app.kubernetes.io/instance: controller-manager-metrics-service 22 | app.kubernetes.io/component: kube-rbac-proxy 23 | app.kubernetes.io/created-by: atlas-operator 24 | app.kubernetes.io/part-of: atlas-operator 25 | app.kubernetes.io/managed-by: kustomize 26 | name: controller-manager-metrics-service 27 | namespace: system 28 | spec: 29 | ports: 30 | - name: https 31 | port: 8443 32 | protocol: TCP 33 | targetPort: https 34 | selector: 35 | control-plane: controller-manager 36 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # All RBAC will be applied under this service account in 16 | # the deployment namespace. You may comment out this resource 17 | # if your manager will use a service account that exists at 18 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 19 | # subjects if changing service account names. 20 | resources: 21 | - service_account.yaml 22 | - role.yaml 23 | - role_binding.yaml 24 | - leader_election_role.yaml 25 | - leader_election_role_binding.yaml 26 | apiVersion: kustomize.config.k8s.io/v1beta1 27 | kind: Kustomization 28 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # permissions to do leader election. 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: Role 18 | metadata: 19 | labels: 20 | app.kubernetes.io/name: role 21 | app.kubernetes.io/instance: leader-election-role 22 | app.kubernetes.io/component: rbac 23 | app.kubernetes.io/created-by: atlas-operator 24 | app.kubernetes.io/part-of: atlas-operator 25 | app.kubernetes.io/managed-by: kustomize 26 | name: leader-election-role 27 | rules: 28 | - apiGroups: 29 | - "" 30 | resources: 31 | - configmaps 32 | verbs: 33 | - get 34 | - list 35 | - watch 36 | - create 37 | - update 38 | - patch 39 | - delete 40 | - apiGroups: 41 | - coordination.k8s.io 42 | resources: 43 | - leases 44 | verbs: 45 | - get 46 | - list 47 | - watch 48 | - create 49 | - update 50 | - patch 51 | - delete 52 | - apiGroups: 53 | - "" 54 | resources: 55 | - events 56 | verbs: 57 | - create 58 | - patch 59 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: RoleBinding 17 | metadata: 18 | labels: 19 | app.kubernetes.io/name: rolebinding 20 | app.kubernetes.io/instance: leader-election-rolebinding 21 | app.kubernetes.io/component: rbac 22 | app.kubernetes.io/created-by: atlas-operator 23 | app.kubernetes.io/part-of: atlas-operator 24 | app.kubernetes.io/managed-by: kustomize 25 | name: leader-election-rolebinding 26 | roleRef: 27 | apiGroup: rbac.authorization.k8s.io 28 | kind: Role 29 | name: leader-election-role 30 | subjects: 31 | - kind: ServiceAccount 32 | name: controller-manager 33 | namespace: system 34 | -------------------------------------------------------------------------------- /config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | --- 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: ClusterRole 17 | metadata: 18 | name: manager-role 19 | rules: 20 | - apiGroups: 21 | - apps 22 | resources: 23 | - deployments 24 | verbs: 25 | - create 26 | - delete 27 | - get 28 | - list 29 | - patch 30 | - update 31 | - watch 32 | - apiGroups: 33 | - "" 34 | resources: 35 | - configmaps 36 | - secrets 37 | verbs: 38 | - create 39 | - delete 40 | - get 41 | - list 42 | - update 43 | - watch 44 | - apiGroups: 45 | - "" 46 | resources: 47 | - events 48 | verbs: 49 | - create 50 | - patch 51 | - apiGroups: 52 | - "" 53 | resources: 54 | - pods 55 | verbs: 56 | - create 57 | - delete 58 | - get 59 | - list 60 | - watch 61 | - apiGroups: 62 | - db.atlasgo.io 63 | resources: 64 | - atlasmigrations 65 | - atlasschemas 66 | verbs: 67 | - create 68 | - delete 69 | - get 70 | - list 71 | - patch 72 | - update 73 | - watch 74 | - apiGroups: 75 | - db.atlasgo.io 76 | resources: 77 | - atlasmigrations/finalizers 78 | - atlasschemas/finalizers 79 | verbs: 80 | - update 81 | - apiGroups: 82 | - db.atlasgo.io 83 | resources: 84 | - atlasmigrations/status 85 | - atlasschemas/status 86 | verbs: 87 | - get 88 | - patch 89 | - update 90 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: ClusterRoleBinding 17 | metadata: 18 | labels: 19 | app.kubernetes.io/name: clusterrolebinding 20 | app.kubernetes.io/instance: manager-rolebinding 21 | app.kubernetes.io/component: rbac 22 | app.kubernetes.io/created-by: atlas-operator 23 | app.kubernetes.io/part-of: atlas-operator 24 | app.kubernetes.io/managed-by: kustomize 25 | name: manager-rolebinding 26 | roleRef: 27 | apiGroup: rbac.authorization.k8s.io 28 | kind: ClusterRole 29 | name: manager-role 30 | subjects: 31 | - kind: ServiceAccount 32 | name: controller-manager 33 | namespace: system 34 | -------------------------------------------------------------------------------- /config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: ServiceAccount 17 | metadata: 18 | labels: 19 | app.kubernetes.io/name: serviceaccount 20 | app.kubernetes.io/instance: controller-manager 21 | app.kubernetes.io/component: rbac 22 | app.kubernetes.io/created-by: atlas-operator 23 | app.kubernetes.io/part-of: atlas-operator 24 | app.kubernetes.io/managed-by: kustomize 25 | name: controller-manager 26 | namespace: system 27 | -------------------------------------------------------------------------------- /config/samples/atlasmigration/cloud/atlas.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: Secret 17 | metadata: 18 | name: atlas-credentials 19 | type: Opaque 20 | stringData: 21 | token: "Atlas Cloud Token" -------------------------------------------------------------------------------- /config/samples/atlasmigration/cloud/resource.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: db.atlasgo.io/v1alpha1 16 | kind: AtlasMigration 17 | metadata: 18 | labels: 19 | app.kubernetes.io/name: atlasmigration 20 | app.kubernetes.io/instance: atlasmigration-sample 21 | app.kubernetes.io/part-of: atlas-operator 22 | app.kubernetes.io/managed-by: kustomize 23 | app.kubernetes.io/created-by: atlas-operator 24 | name: atlasmigration-sample 25 | spec: 26 | env: my-env 27 | urlFrom: 28 | secretKeyRef: 29 | key: url 30 | name: db-credentials 31 | cloud: 32 | url: https://example.atlasgo.link/ 33 | tokenFrom: 34 | secretKeyRef: 35 | key: token 36 | name: atlas-credentials 37 | project: "atlas" 38 | dir: 39 | remote: 40 | name: "atlas" 41 | tag: "" 42 | 43 | -------------------------------------------------------------------------------- /config/samples/atlasmigration/default/dir.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: ConfigMap 17 | metadata: 18 | name: migration-dir 19 | data: 20 | 20230316085611.sql: | 21 | -- Create "users" table 22 | CREATE TABLE `users` ( 23 | `id` int NOT NULL, 24 | `user_name` varchar(255) NOT NULL, 25 | `email` varchar(255) NOT NULL, 26 | PRIMARY KEY (`id`) 27 | ) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci; 28 | 20230316090502.sql: | 29 | -- Create "posts" table 30 | CREATE TABLE `posts` ( 31 | `id` int NOT NULL, 32 | `user_id` int NOT NULL, 33 | `title` varchar(255) NOT NULL, 34 | `body` text NOT NULL, 35 | PRIMARY KEY (`id`), 36 | INDEX `user_id` (`user_id`), 37 | CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE 38 | ) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci; 39 | atlas.sum: | 40 | h1:XBXbh+rzLis8gknjlIqnxXLBkOZ+sN2v2p7KjyVFYYM= 41 | 20230316085611.sql h1:br6W6LPEnnsejlz/7hRm9zthwStCzjN2vZkqVPxlmvo= 42 | 20230316090502.sql h1:GfeRjkSeoCt3JVRtLQNa/r50lRfpAPXS7AqTU2ZNFgY= 43 | -------------------------------------------------------------------------------- /config/samples/atlasmigration/default/resource.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: db.atlasgo.io/v1alpha1 16 | kind: AtlasMigration 17 | metadata: 18 | labels: 19 | app.kubernetes.io/name: atlasmigration 20 | app.kubernetes.io/instance: atlasmigration-sample 21 | app.kubernetes.io/part-of: atlas-operator 22 | app.kubernetes.io/managed-by: kustomize 23 | app.kubernetes.io/created-by: atlas-operator 24 | name: atlasmigration-sample 25 | spec: 26 | urlFrom: 27 | secretKeyRef: 28 | key: url 29 | name: db-credentials 30 | dir: 31 | configMapRef: 32 | name: "migration-dir" 33 | 34 | -------------------------------------------------------------------------------- /config/samples/atlasmigration/local/resource.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: db.atlasgo.io/v1alpha1 16 | kind: AtlasMigration 17 | metadata: 18 | labels: 19 | app.kubernetes.io/name: atlasmigration 20 | app.kubernetes.io/instance: atlasmigration-sample 21 | app.kubernetes.io/part-of: atlas-operator 22 | app.kubernetes.io/managed-by: kustomize 23 | app.kubernetes.io/created-by: atlas-operator 24 | name: atlasmigration-sample 25 | spec: 26 | urlFrom: 27 | secretKeyRef: 28 | key: url 29 | name: db-credentials 30 | dir: 31 | local: 32 | 20230316085611.sql: | 33 | -- Create "users" table 34 | CREATE TABLE `users` ( 35 | `id` int NOT NULL, 36 | `user_name` varchar(255) NOT NULL, 37 | `email` varchar(255) NOT NULL, 38 | PRIMARY KEY (`id`) 39 | ) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci; 40 | 20230316090502.sql: | 41 | -- Create "posts" table 42 | CREATE TABLE `posts` ( 43 | `id` int NOT NULL, 44 | `user_id` int NOT NULL, 45 | `title` varchar(255) NOT NULL, 46 | `body` text NOT NULL, 47 | PRIMARY KEY (`id`), 48 | INDEX `user_id` (`user_id`), 49 | CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE 50 | ) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci; 51 | atlas.sum: | 52 | h1:XBXbh+rzLis8gknjlIqnxXLBkOZ+sN2v2p7KjyVFYYM= 53 | 20230316085611.sql h1:br6W6LPEnnsejlz/7hRm9zthwStCzjN2vZkqVPxlmvo= 54 | 20230316090502.sql h1:GfeRjkSeoCt3JVRtLQNa/r50lRfpAPXS7AqTU2ZNFgY= 55 | 56 | -------------------------------------------------------------------------------- /config/samples/db_v1alpha1_atlasschema.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: db.atlasgo.io/v1alpha1 16 | kind: AtlasSchema 17 | metadata: 18 | labels: 19 | app.kubernetes.io/name: atlasschema 20 | app.kubernetes.io/instance: atlasschema-sample 21 | app.kubernetes.io/part-of: atlas-operator 22 | app.kubernetes.io/managed-by: kustomize 23 | app.kubernetes.io/created-by: atlas-operator 24 | name: atlasschema-sample 25 | spec: 26 | urlFrom: 27 | secretKeyRef: 28 | key: url 29 | name: db-credentials 30 | schema: 31 | sql: | 32 | create table users ( 33 | id int not null auto_increment, 34 | name varchar(255) not null, 35 | email varchar(255) unique not null, 36 | primary key (id) 37 | ); 38 | -------------------------------------------------------------------------------- /config/samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ## Append samples you want in your CSV to this file as resources ## 16 | resources: 17 | - db_v1alpha1_atlasschema.yaml 18 | apiVersion: kustomize.config.k8s.io/v1beta1 19 | kind: Kustomization 20 | -------------------------------------------------------------------------------- /config/scorecard/bases/config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: scorecard.operatorframework.io/v1alpha3 16 | kind: Configuration 17 | metadata: 18 | name: config 19 | stages: 20 | - parallel: true 21 | tests: [] 22 | -------------------------------------------------------------------------------- /config/scorecard/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | resources: 16 | - bases/config.yaml 17 | apiVersion: kustomize.config.k8s.io/v1beta1 18 | kind: Kustomization 19 | patches: 20 | - path: patches/basic.config.yaml 21 | target: 22 | group: scorecard.operatorframework.io 23 | kind: Configuration 24 | name: config 25 | version: v1alpha3 26 | - path: patches/olm.config.yaml 27 | target: 28 | group: scorecard.operatorframework.io 29 | kind: Configuration 30 | name: config 31 | version: v1alpha3 32 | -------------------------------------------------------------------------------- /config/scorecard/patches/basic.config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | - op: add 16 | path: /stages/0/tests/- 17 | value: 18 | entrypoint: 19 | - scorecard-test 20 | - basic-check-spec 21 | image: quay.io/operator-framework/scorecard-test:v1.28.0 22 | labels: 23 | suite: basic 24 | test: basic-check-spec-test 25 | -------------------------------------------------------------------------------- /config/scorecard/patches/olm.config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | - op: add 16 | path: /stages/0/tests/- 17 | value: 18 | entrypoint: 19 | - scorecard-test 20 | - olm-bundle-validation 21 | image: quay.io/operator-framework/scorecard-test:v1.28.0 22 | labels: 23 | suite: olm 24 | test: olm-bundle-validation-test 25 | - op: add 26 | path: /stages/0/tests/- 27 | value: 28 | entrypoint: 29 | - scorecard-test 30 | - olm-crds-have-validation 31 | image: quay.io/operator-framework/scorecard-test:v1.28.0 32 | labels: 33 | suite: olm 34 | test: olm-crds-have-validation-test 35 | - op: add 36 | path: /stages/0/tests/- 37 | value: 38 | entrypoint: 39 | - scorecard-test 40 | - olm-crds-have-resources 41 | image: quay.io/operator-framework/scorecard-test:v1.28.0 42 | labels: 43 | suite: olm 44 | test: olm-crds-have-resources-test 45 | - op: add 46 | path: /stages/0/tests/- 47 | value: 48 | entrypoint: 49 | - scorecard-test 50 | - olm-spec-descriptors 51 | image: quay.io/operator-framework/scorecard-test:v1.28.0 52 | labels: 53 | suite: olm 54 | test: olm-spec-descriptors-test 55 | - op: add 56 | path: /stages/0/tests/- 57 | value: 58 | entrypoint: 59 | - scorecard-test 60 | - olm-status-descriptors 61 | image: quay.io/operator-framework/scorecard-test:v1.28.0 62 | labels: 63 | suite: olm 64 | test: olm-status-descriptors-test 65 | -------------------------------------------------------------------------------- /config/sqlserver/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | namespace: atlas-operator-system 16 | apiVersion: kustomize.config.k8s.io/v1beta1 17 | kind: Kustomization 18 | resources: 19 | - ../default 20 | patches: 21 | - target: 22 | kind: Deployment 23 | namespace: system 24 | name: controller-manager 25 | patch: |- 26 | - op: add 27 | path: "/spec/template/spec/containers/0/env/-" 28 | value: 29 | name: MSSQL_ACCEPT_EULA 30 | value: "Y" 31 | - op: add 32 | path: "/spec/template/spec/containers/0/env/-" 33 | value: 34 | name: MSSQL_PID 35 | value: "Developer" 36 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ariga/atlas-operator 2 | 3 | go 1.23 4 | 5 | toolchain go1.23.2 6 | 7 | require ( 8 | ariga.io/atlas v0.28.1 9 | ariga.io/atlas-go-sdk v0.6.7 10 | github.com/hashicorp/hcl/v2 v2.18.1 11 | github.com/rogpeppe/go-internal v1.13.1 12 | github.com/stretchr/testify v1.9.0 13 | github.com/zclconf/go-cty v1.14.4 14 | golang.org/x/mod v0.21.0 15 | k8s.io/api v0.31.0 16 | k8s.io/apimachinery v0.31.1 17 | k8s.io/client-go v0.31.0 18 | k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 19 | sigs.k8s.io/controller-runtime v0.19.0 20 | ) 21 | 22 | require ( 23 | github.com/agext/levenshtein v1.2.3 // indirect 24 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect 25 | github.com/beorn7/perks v1.0.1 // indirect 26 | github.com/bmatcuk/doublestar v1.3.4 // indirect 27 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 28 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 29 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 30 | github.com/evanphx/json-patch v4.12.0+incompatible // indirect 31 | github.com/evanphx/json-patch/v5 v5.9.0 // indirect 32 | github.com/fsnotify/fsnotify v1.7.0 // indirect 33 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect 34 | github.com/go-logr/logr v1.4.2 // indirect 35 | github.com/go-logr/zapr v1.3.0 // indirect 36 | github.com/go-openapi/inflect v0.19.0 // indirect 37 | github.com/go-openapi/jsonpointer v0.19.6 // indirect 38 | github.com/go-openapi/jsonreference v0.20.2 // indirect 39 | github.com/go-openapi/swag v0.22.4 // indirect 40 | github.com/gogo/protobuf v1.3.2 // indirect 41 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 42 | github.com/golang/protobuf v1.5.4 // indirect 43 | github.com/google/gnostic-models v0.6.8 // indirect 44 | github.com/google/go-cmp v0.6.0 // indirect 45 | github.com/google/gofuzz v1.2.0 // indirect 46 | github.com/google/uuid v1.6.0 // indirect 47 | github.com/imdario/mergo v0.3.6 // indirect 48 | github.com/josharian/intern v1.0.0 // indirect 49 | github.com/json-iterator/go v1.1.12 // indirect 50 | github.com/mailru/easyjson v0.7.7 // indirect 51 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 52 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 53 | github.com/modern-go/reflect2 v1.0.2 // indirect 54 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 55 | github.com/pkg/errors v0.9.1 // indirect 56 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 57 | github.com/prometheus/client_golang v1.19.1 // indirect 58 | github.com/prometheus/client_model v0.6.1 // indirect 59 | github.com/prometheus/common v0.55.0 // indirect 60 | github.com/prometheus/procfs v0.15.1 // indirect 61 | github.com/spf13/pflag v1.0.5 // indirect 62 | github.com/x448/float16 v0.8.4 // indirect 63 | go.uber.org/multierr v1.11.0 // indirect 64 | go.uber.org/zap v1.26.0 // indirect 65 | golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect 66 | golang.org/x/net v0.26.0 // indirect 67 | golang.org/x/oauth2 v0.21.0 // indirect 68 | golang.org/x/sys v0.21.0 // indirect 69 | golang.org/x/term v0.21.0 // indirect 70 | golang.org/x/text v0.16.0 // indirect 71 | golang.org/x/time v0.3.0 // indirect 72 | golang.org/x/tools v0.22.0 // indirect 73 | gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect 74 | google.golang.org/protobuf v1.35.1 // indirect 75 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect 76 | gopkg.in/inf.v0 v0.9.1 // indirect 77 | gopkg.in/yaml.v2 v2.4.0 // indirect 78 | gopkg.in/yaml.v3 v3.0.1 // indirect 79 | k8s.io/apiextensions-apiserver v0.31.0 // indirect 80 | k8s.io/klog/v2 v2.130.1 // indirect 81 | k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect 82 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect 83 | sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect 84 | sigs.k8s.io/yaml v1.4.0 // indirect 85 | ) 86 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Atlas Operator Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | -------------------------------------------------------------------------------- /hack/boilerplate.yaml.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /internal/controller/common_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Atlas Operator Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controller 16 | 17 | import ( 18 | "reflect" 19 | "testing" 20 | "time" 21 | 22 | "github.com/hashicorp/hcl/v2" 23 | "github.com/hashicorp/hcl/v2/hclwrite" 24 | ) 25 | 26 | func Test_mergeBlocks(t *testing.T) { 27 | type args struct { 28 | atlasEnv string 29 | dst string 30 | src string 31 | } 32 | tests := []struct { 33 | name string 34 | args args 35 | expected string 36 | }{ 37 | { 38 | name: "empty", 39 | args: args{ 40 | dst: "", 41 | src: "", 42 | }, 43 | expected: "", 44 | }, 45 | { 46 | name: "dst empty", 47 | args: args{ 48 | dst: "", 49 | src: `env "example" {}`, 50 | }, 51 | expected: ` 52 | env "example" {}`, 53 | }, 54 | { 55 | name: "same block", 56 | args: args{ 57 | dst: `env "example" {}`, 58 | src: `env "example" {}`, 59 | }, 60 | expected: `env "example" {}`, 61 | }, 62 | { 63 | name: "different block", 64 | args: args{ 65 | dst: `env "example" {}`, 66 | src: `env "example2" {}`, 67 | }, 68 | expected: `env "example" {} 69 | env "example2" {}`, 70 | }, 71 | { 72 | name: "same block with different attributes", 73 | args: args{ 74 | dst: ` 75 | env "example" { 76 | key = "value" 77 | }`, 78 | src: ` 79 | env "example" { 80 | key2 = "value2" 81 | }`, 82 | }, 83 | expected: ` 84 | env "example" { 85 | key = "value" 86 | key2 = "value2" 87 | }`, 88 | }, 89 | { 90 | name: "same block with same attributes", 91 | args: args{ 92 | dst: ` 93 | env "example" { 94 | key = "value" 95 | }`, 96 | src: ` 97 | env "example" { 98 | key = "value2" 99 | }`, 100 | }, 101 | expected: ` 102 | env "example" { 103 | key = "value2" 104 | }`, 105 | }, 106 | { 107 | name: "merge unnamed blocks", 108 | args: args{ 109 | dst: ` 110 | env { 111 | name = atlas.env 112 | key = "value" 113 | }`, 114 | src: ` 115 | env { 116 | name = atlas.env 117 | key2 = "value2" 118 | } 119 | `, 120 | }, 121 | expected: ` 122 | env { 123 | name = atlas.env 124 | key = "value" 125 | key2 = "value2" 126 | }`, 127 | }, 128 | { 129 | name: "merge named block to unnamed block", 130 | args: args{ 131 | dst: ` 132 | env { 133 | name = atlas.env 134 | key = "value" 135 | }`, 136 | src: ` 137 | env "example" { 138 | key2 = "value2" 139 | } 140 | `, 141 | }, 142 | expected: ` 143 | env { 144 | name = atlas.env 145 | key = "value" 146 | key2 = "value2" 147 | }`, 148 | }, 149 | { 150 | name: "merge unnamed block to named block", 151 | args: args{ 152 | atlasEnv: "example", 153 | dst: ` 154 | env "example" { 155 | key = "value" 156 | }`, 157 | src: ` 158 | env { 159 | name = atlas.env 160 | key2 = "value2" 161 | } 162 | `, 163 | }, 164 | expected: ` 165 | env "example" { 166 | key = "value" 167 | key2 = "value2" 168 | }`, 169 | }, 170 | } 171 | for _, tt := range tests { 172 | t.Run(tt.name, func(t *testing.T) { 173 | dst, _ := hclwrite.ParseConfig([]byte(tt.args.dst), "", hcl.InitialPos) 174 | src, _ := hclwrite.ParseConfig([]byte(tt.args.src), "", hcl.InitialPos) 175 | mergeBlocks(dst.Body(), src.Body(), tt.args.atlasEnv) 176 | if got := string(dst.Bytes()); got != tt.expected { 177 | t.Errorf("mergeBlocks() = %v, want %v", got, tt.expected) 178 | } 179 | }) 180 | } 181 | } 182 | 183 | func Test_backoffDelayAt(t *testing.T) { 184 | type args struct { 185 | retry int 186 | } 187 | tests := []struct { 188 | name string 189 | args args 190 | want time.Duration 191 | }{ 192 | { 193 | name: "0", 194 | args: args{ 195 | retry: 0, 196 | }, 197 | want: 0, 198 | }, 199 | { 200 | name: "1", 201 | args: args{ 202 | retry: 1, 203 | }, 204 | want: 5 * time.Second, 205 | }, 206 | { 207 | name: "2", 208 | args: args{ 209 | retry: 2, 210 | }, 211 | want: 10 * time.Second, 212 | }, 213 | { 214 | name: "20", 215 | args: args{ 216 | retry: 20, 217 | }, 218 | want: 100 * time.Second, 219 | }, 220 | } 221 | for _, tt := range tests { 222 | t.Run(tt.name, func(t *testing.T) { 223 | if got := backoffDelayAt(tt.args.retry); !reflect.DeepEqual(got, tt.want) { 224 | t.Errorf("backoffDelayAt() = %v, want %v", got, tt.want) 225 | } 226 | }) 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /internal/controller/lint.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Atlas Operator Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controller 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | "os" 21 | "strings" 22 | 23 | "ariga.io/atlas-go-sdk/atlasexec" 24 | "ariga.io/atlas/sql/sqlcheck" 25 | "sigs.k8s.io/controller-runtime/pkg/log" 26 | ) 27 | 28 | const lintDirName = "lint-migrations" 29 | 30 | // lint run `atlas migrate lint` to check for destructive changes. 31 | // It returns a destructiveErr if destructive changes are detected. 32 | // 33 | // It works by creating two versions of migration: 34 | // - 1.sql: the current schema. 35 | // - 2.sql: the pending changes. 36 | // Then it runs `atlas migrate lint` in the temporary directory. 37 | func (r *AtlasSchemaReconciler) lint(ctx context.Context, wd *atlasexec.WorkingDir, data *managedData, vars atlasexec.VarArgs) error { 38 | cli, err := r.atlasClient(wd.Path(), data.Cloud) 39 | if err != nil { 40 | return err 41 | } 42 | current, err := cli.SchemaInspect(ctx, &atlasexec.SchemaInspectParams{ 43 | Env: data.EnvName, 44 | Vars: vars, 45 | Format: "{{ sql . }}", 46 | }) 47 | if err != nil { 48 | return err 49 | } 50 | plans, err := cli.SchemaApplySlice(ctx, &atlasexec.SchemaApplyParams{ 51 | Env: data.EnvName, 52 | Vars: vars, 53 | To: data.targetURL(), 54 | DryRun: true, // Dry run to get pending changes. 55 | }) 56 | if err != nil { 57 | return err 58 | } 59 | defer func() { 60 | dir := wd.Path(lintDirName) 61 | if err := os.RemoveAll(dir); err != nil { 62 | log.FromContext(ctx).Error(err, 63 | "unable to remove temporary directory", "dir", dir) 64 | } 65 | }() 66 | if len(plans) != 1 { 67 | return fmt.Errorf("unexpected number of schema plans: %d", len(plans)) 68 | } 69 | dir, err := memDir(map[string]string{ 70 | "1.sql": current, 71 | "2.sql": strings.Join(plans[0].Changes.Pending, ";\n"), 72 | }) 73 | if err != nil { 74 | return err 75 | } 76 | err = wd.CopyFS(lintDirName, dir) 77 | if err != nil { 78 | return err 79 | } 80 | lint, err := cli.MigrateLint(ctx, &atlasexec.MigrateLintParams{ 81 | Env: data.EnvName, 82 | Vars: vars, 83 | DirURL: fmt.Sprintf("file://./%s", lintDirName), 84 | Latest: 1, // Only lint 2.sql, pending changes. 85 | }) 86 | if err != nil { 87 | return err 88 | } 89 | return destructive(lint) 90 | } 91 | 92 | func destructive(rep *atlasexec.SummaryReport) error { 93 | var checks []sqlcheck.Diagnostic 94 | for _, f := range rep.Files { 95 | for _, r := range f.Reports { 96 | if f.Error == "" { 97 | continue 98 | } 99 | for _, diag := range r.Diagnostics { 100 | if strings.HasPrefix(diag.Code, "DS") { 101 | checks = append(checks, diag) 102 | } 103 | } 104 | } 105 | } 106 | if len(checks) > 0 { 107 | return &destructiveErr{diags: checks} 108 | } 109 | return nil 110 | } 111 | 112 | type destructiveErr struct { 113 | diags []sqlcheck.Diagnostic 114 | } 115 | 116 | func (d *destructiveErr) Error() string { 117 | var buf strings.Builder 118 | buf.WriteString("destructive changes detected:\n") 119 | for _, diag := range d.diags { 120 | buf.WriteString("- " + diag.Text + "\n") 121 | } 122 | return buf.String() 123 | } 124 | 125 | func (d *destructiveErr) FirstRun() (string, string) { 126 | return "FirstRunDestructive", d.Error() + "\n" + 127 | "To prevent accidental drop of resources, first run of a schema must not contain destructive changes.\n" + 128 | "Read more: https://atlasgo.io/integrations/kubernetes/#destructive-changes" 129 | } 130 | -------------------------------------------------------------------------------- /internal/controller/watch/watch.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Atlas Operator Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package watch 16 | 17 | import ( 18 | "context" 19 | "slices" 20 | 21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | "k8s.io/apimachinery/pkg/types" 23 | "k8s.io/client-go/util/workqueue" 24 | "sigs.k8s.io/controller-runtime/pkg/event" 25 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 26 | ) 27 | 28 | // ResourceWatcher implements handler.EventHandler and is used to trigger reconciliation when 29 | // a watched object changes. It's designed to only be used for a single type of object. 30 | // If multiple types should be watched, one ResourceWatcher for each type should be used. 31 | type ResourceWatcher struct { 32 | watched map[types.NamespacedName][]types.NamespacedName 33 | } 34 | 35 | type Queue = workqueue.TypedRateLimitingInterface[reconcile.Request] 36 | 37 | // New will create a new ResourceWatcher with no watched objects. 38 | func New() *ResourceWatcher { 39 | return &ResourceWatcher{ 40 | watched: make(map[types.NamespacedName][]types.NamespacedName), 41 | } 42 | } 43 | 44 | // Watch will add a new object to watch. 45 | func (w ResourceWatcher) Watch(watchedName, dependentName types.NamespacedName) { 46 | // Check if resource is already being watched. 47 | existing := w.watched[watchedName] 48 | if slices.Contains(existing, dependentName) { 49 | return 50 | } 51 | w.watched[watchedName] = append(existing, dependentName) 52 | } 53 | 54 | func (w ResourceWatcher) Read(watchedName types.NamespacedName) []types.NamespacedName { 55 | return w.watched[watchedName] 56 | } 57 | 58 | func (w ResourceWatcher) Create(_ context.Context, event event.CreateEvent, queue Queue) { 59 | w.handleEvent(event.Object, queue) 60 | } 61 | 62 | func (w ResourceWatcher) Update(_ context.Context, event event.UpdateEvent, queue Queue) { 63 | w.handleEvent(event.ObjectOld, queue) 64 | } 65 | 66 | func (w ResourceWatcher) Delete(_ context.Context, event event.DeleteEvent, queue Queue) { 67 | w.handleEvent(event.Object, queue) 68 | } 69 | 70 | func (w ResourceWatcher) Generic(_ context.Context, event event.GenericEvent, queue Queue) { 71 | w.handleEvent(event.Object, queue) 72 | } 73 | 74 | // handleEvent is called when an event is received for an object. 75 | // It will check if the object is being watched and trigger a reconciliation for 76 | // the dependent object. 77 | func (w ResourceWatcher) handleEvent(meta metav1.Object, queue Queue) { 78 | changedObjectName := types.NamespacedName{ 79 | Name: meta.GetName(), 80 | Namespace: meta.GetNamespace(), 81 | } 82 | // Enqueue reconciliation for each dependent object. 83 | for _, reconciledObjectName := range w.watched[changedObjectName] { 84 | queue.Add(reconcile.Request{ 85 | NamespacedName: reconciledObjectName, 86 | }) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /internal/controller/watch/watch_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Atlas Operator Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package watch 16 | 17 | import ( 18 | "context" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/assert" 22 | corev1 "k8s.io/api/core/v1" 23 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | "k8s.io/apimachinery/pkg/types" 25 | "k8s.io/client-go/util/workqueue" 26 | "sigs.k8s.io/controller-runtime/pkg/controller/controllertest" 27 | "sigs.k8s.io/controller-runtime/pkg/event" 28 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 29 | 30 | dbv1alpha1 "github.com/ariga/atlas-operator/api/v1alpha1" 31 | ) 32 | 33 | func TestWatcher(t *testing.T) { 34 | obj := &corev1.Pod{ 35 | ObjectMeta: metav1.ObjectMeta{ 36 | Name: "pod", 37 | Namespace: "namespace", 38 | }, 39 | } 40 | objNsName := types.NamespacedName{Name: obj.Name, Namespace: obj.Namespace} 41 | 42 | mdb1 := dbv1alpha1.AtlasMigration{ 43 | ObjectMeta: metav1.ObjectMeta{ 44 | Name: "mdb1", 45 | Namespace: "namespace", 46 | }, 47 | } 48 | mdb2 := dbv1alpha1.AtlasMigration{ 49 | ObjectMeta: metav1.ObjectMeta{ 50 | Name: "mdb2", 51 | Namespace: "namespace", 52 | }, 53 | } 54 | t.Run("Non-watched object", func(t *testing.T) { 55 | watcher := New() 56 | queue := newQueue() 57 | 58 | watcher.Create(context.Background(), event.CreateEvent{ 59 | Object: obj, 60 | }, queue) 61 | 62 | // Ensure no reconciliation is queued if object is not watched. 63 | assert.Equal(t, 0, queue.Len()) 64 | }) 65 | 66 | t.Run("Multiple objects to reconcile", func(t *testing.T) { 67 | watcher := New() 68 | queue := newQueue() 69 | watcher.Watch(objNsName, mdb1.NamespacedName()) 70 | watcher.Watch(objNsName, mdb2.NamespacedName()) 71 | 72 | watcher.Create(context.Background(), event.CreateEvent{ 73 | Object: obj, 74 | }, queue) 75 | 76 | // Ensure multiple reconciliations are enqueued. 77 | assert.Equal(t, 2, queue.Len()) 78 | }) 79 | 80 | t.Run("Create event", func(t *testing.T) { 81 | watcher := New() 82 | queue := newQueue() 83 | watcher.Watch(objNsName, mdb1.NamespacedName()) 84 | 85 | watcher.Create(context.Background(), event.CreateEvent{ 86 | Object: obj, 87 | }, queue) 88 | 89 | assert.Equal(t, 1, queue.Len()) 90 | }) 91 | 92 | t.Run("Update event", func(t *testing.T) { 93 | watcher := New() 94 | queue := newQueue() 95 | watcher.Watch(objNsName, mdb1.NamespacedName()) 96 | 97 | watcher.Update(context.Background(), event.UpdateEvent{ 98 | ObjectOld: obj, 99 | ObjectNew: obj, 100 | }, queue) 101 | 102 | assert.Equal(t, 1, queue.Len()) 103 | }) 104 | 105 | t.Run("Delete event", func(t *testing.T) { 106 | watcher := New() 107 | queue := newQueue() 108 | watcher.Watch(objNsName, mdb1.NamespacedName()) 109 | 110 | watcher.Delete(context.Background(), event.DeleteEvent{ 111 | Object: obj, 112 | }, queue) 113 | 114 | assert.Equal(t, 1, queue.Len()) 115 | }) 116 | 117 | t.Run("Generic event", func(t *testing.T) { 118 | watcher := New() 119 | queue := newQueue() 120 | watcher.Watch(objNsName, mdb1.NamespacedName()) 121 | 122 | watcher.Generic(context.Background(), event.GenericEvent{ 123 | Object: obj, 124 | }, queue) 125 | 126 | assert.Equal(t, 1, queue.Len()) 127 | }) 128 | } 129 | 130 | func TestWatcherAdd(t *testing.T) { 131 | watcher := New() 132 | assert.Empty(t, watcher.watched) 133 | 134 | watchedName := types.NamespacedName{Name: "object", Namespace: "namespace"} 135 | 136 | mdb1 := dbv1alpha1.AtlasMigration{ 137 | ObjectMeta: metav1.ObjectMeta{ 138 | Name: "mdb1", 139 | Namespace: "namespace", 140 | }, 141 | } 142 | mdb2 := dbv1alpha1.AtlasMigration{ 143 | ObjectMeta: metav1.ObjectMeta{ 144 | Name: "mdb2", 145 | Namespace: "namespace", 146 | }, 147 | } 148 | 149 | // Ensure single object can be added to empty watchlist. 150 | watcher.Watch(watchedName, mdb1.NamespacedName()) 151 | assert.Len(t, watcher.watched, 1) 152 | assert.Equal(t, []types.NamespacedName{mdb1.NamespacedName()}, watcher.watched[watchedName]) 153 | 154 | // Ensure object can only be watched once. 155 | watcher.Watch(watchedName, mdb1.NamespacedName()) 156 | assert.Len(t, watcher.watched, 1) 157 | assert.Equal(t, []types.NamespacedName{mdb1.NamespacedName()}, watcher.watched[watchedName]) 158 | 159 | // Ensure a single object can be watched for multiple reconciliations. 160 | watcher.Watch(watchedName, mdb2.NamespacedName()) 161 | assert.Len(t, watcher.watched, 1) 162 | assert.Equal(t, []types.NamespacedName{ 163 | mdb1.NamespacedName(), 164 | mdb2.NamespacedName(), 165 | }, watcher.watched[watchedName]) 166 | } 167 | 168 | func newQueue() *controllertest.Queue { 169 | return &controllertest.Queue{ 170 | TypedInterface: workqueue.NewTyped[reconcile.Request](), 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /internal/vercheck/notification.tmpl: -------------------------------------------------------------------------------- 1 | {{- with .Advisory -}} 2 | SECURITY ADVISORY 3 | {{ .Text }} 4 | {{- end }} 5 | {{- with .Latest -}} 6 | A new version of the Atlas Operator is available ({{ .Version }}){{ with .Link }}: {{ . }}{{ end }} 7 | {{- with .Summary }} 8 | {{ . }} 9 | {{- end }} 10 | {{- end }} -------------------------------------------------------------------------------- /internal/vercheck/vercheck.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021-present The Atlas Authors. All rights reserved. 2 | // This source code is licensed under the Apache 2.0 license found 3 | // in the LICENSE file in the root directory of this source tree. 4 | 5 | package vercheck 6 | 7 | import ( 8 | _ "embed" 9 | "encoding/json" 10 | "errors" 11 | "fmt" 12 | "net/http" 13 | "net/url" 14 | "os" 15 | "runtime" 16 | "text/template" 17 | "time" 18 | ) 19 | 20 | // New returns a new VerChecker for the endpoint. 21 | func New(endpoint, statePath string) *VerChecker { 22 | return &VerChecker{endpoint: endpoint, statePath: statePath} 23 | } 24 | 25 | type ( 26 | // Latest contains information about the most recent version. 27 | Latest struct { 28 | // Version is the new version name. 29 | Version string 30 | // Summary contains a brief description of the new version. 31 | Summary string 32 | // Link is a URL to a web page describing the new version. 33 | Link string 34 | } 35 | // Advisory contains contents of security advisories. 36 | Advisory struct { 37 | Text string `json:"text"` 38 | } 39 | // Payload returns information to the client about their existing version of a component. 40 | Payload struct { 41 | // Latest is set if there is a newer version to upgrade to. 42 | Latest *Latest `json:"latest"` 43 | // Advisory is set if security advisories exist for the current version. 44 | Advisory *Advisory `json:"advisory"` 45 | } 46 | // VerChecker retrieves version information from the vercheck service. 47 | VerChecker struct { 48 | endpoint string 49 | statePath string 50 | } 51 | // State stores information about local runs of VerChecker to limit the 52 | // frequency in which clients poll the service for information. 53 | State struct { 54 | CheckedAt time.Time `json:"checkedat"` 55 | } 56 | ) 57 | 58 | var ( 59 | // errSkip is returned when check isn't run because 24 hours haven't passed from the previous run. 60 | errSkip = errors.New("skip vercheck") 61 | // Notify is the template for displaying the payload to the user. 62 | Notify *template.Template 63 | ) 64 | 65 | // Check makes an HTTP request to endpoint to check if a new version or security advisories 66 | // exist for the current version. Check tries to read the latest time it was run from the 67 | // statePath, if found and 24 hours have not passed the check is skipped. When done, the latest 68 | // time is updated in statePath. 69 | func (v *VerChecker) Check(ver string) (*Payload, error) { 70 | if err := v.verifyTime(); err != nil { 71 | return nil, err 72 | } 73 | endpoint, err := url.JoinPath(v.endpoint, "atlas-operator", ver) 74 | if err != nil { 75 | return nil, err 76 | } 77 | req, err := http.NewRequest(http.MethodGet, endpoint, nil) 78 | if err != nil { 79 | return nil, err 80 | } 81 | req.Header.Set("User-Agent", fmt.Sprintf("Ariga-Atlas-Operator/%s (%s, %s)", ver, runtime.GOOS, runtime.GOARCH)) 82 | resp, err := http.DefaultClient.Do(req) 83 | if err != nil { 84 | return nil, err 85 | } 86 | if resp.StatusCode != http.StatusOK { 87 | return nil, fmt.Errorf("status: %s", resp.Status) 88 | } 89 | var p Payload 90 | if err := json.NewDecoder(resp.Body).Decode(&p); err != nil { 91 | return nil, err 92 | } 93 | if v.statePath != "" { 94 | s := State{CheckedAt: time.Now()} 95 | st, err := json.Marshal(s) 96 | if err != nil { 97 | return nil, err 98 | } 99 | if err := os.WriteFile(v.statePath, st, os.ModePerm); err != nil { 100 | return nil, err 101 | } 102 | } 103 | return &p, nil 104 | } 105 | 106 | func (v *VerChecker) verifyTime() error { 107 | // Skip check if path to state file isn't configured. 108 | if v.statePath == "" { 109 | return nil 110 | } 111 | var s State 112 | f, err := os.Open(v.statePath) 113 | if err != nil { 114 | return nil 115 | } 116 | if err := json.NewDecoder(f).Decode(&s); err != nil { 117 | return nil 118 | } 119 | if time.Since(s.CheckedAt) >= (time.Hour * 24) { 120 | return nil 121 | } 122 | return errSkip 123 | } 124 | 125 | //go:embed notification.tmpl 126 | var notifyTmpl string 127 | 128 | func init() { 129 | var err error 130 | Notify, err = template.New("").Parse(notifyTmpl) 131 | if err != nil { 132 | panic(err) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /internal/vercheck/vercheck_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021-present The Atlas Authors. All rights reserved. 2 | // This source code is licensed under the Apache 2.0 license found 3 | // in the LICENSE file in the root directory of this source tree. 4 | 5 | package vercheck 6 | 7 | import ( 8 | "bytes" 9 | "encoding/json" 10 | "net/http" 11 | "net/http/httptest" 12 | "os" 13 | "path/filepath" 14 | "testing" 15 | "time" 16 | 17 | "github.com/stretchr/testify/require" 18 | ) 19 | 20 | func TestVerCheck(t *testing.T) { 21 | var path, ua string 22 | srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 23 | output := `{"latest":{"Version":"v0.7.2","Summary":"","Link":"https://github.com/ariga/atlas/releases/tag/v0.7.2"},"advisory":null}` 24 | path = r.URL.Path 25 | ua = r.Header.Get("User-Agent") 26 | _, _ = w.Write([]byte(output)) 27 | })) 28 | defer srv.Close() 29 | 30 | vc := New(srv.URL, "") 31 | check, err := vc.Check("v0.1.2") 32 | 33 | require.EqualValues(t, "/atlas-operator/v0.1.2", path) 34 | require.Contains(t, ua, "Ariga-Atlas-Operator") 35 | require.NoError(t, err) 36 | require.EqualValues(t, &Payload{ 37 | Latest: &Latest{ 38 | Version: "v0.7.2", 39 | Summary: "", 40 | Link: "https://github.com/ariga/atlas/releases/tag/v0.7.2", 41 | }, 42 | }, check) 43 | } 44 | 45 | func TestState(t *testing.T) { 46 | hrAgo, err := json.Marshal(State{CheckedAt: time.Now().Add(-time.Hour)}) 47 | require.NoError(t, err) 48 | weekAgo, err := json.Marshal(State{CheckedAt: time.Now().Add(-time.Hour * 24 * 7)}) 49 | require.NoError(t, err) 50 | for _, tt := range []struct { 51 | name string 52 | state string 53 | expectedRun bool 54 | }{ 55 | { 56 | name: "corrupt json", 57 | state: "{", 58 | expectedRun: true, 59 | }, 60 | { 61 | name: "none", 62 | state: "", // no file 63 | expectedRun: true, 64 | }, 65 | { 66 | name: "one hr ago", 67 | state: string(hrAgo), 68 | expectedRun: false, 69 | }, 70 | { 71 | name: "one week ago", 72 | state: string(weekAgo), 73 | expectedRun: true, 74 | }, 75 | } { 76 | t.Run(tt.name, func(t *testing.T) { 77 | var ran bool 78 | srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 79 | ran = true 80 | _, _ = w.Write([]byte(`{}`)) 81 | })) 82 | t.Cleanup(srv.Close) 83 | path := filepath.Join(t.TempDir(), "release.json") 84 | if tt.state != "" { 85 | err := os.WriteFile(path, []byte(tt.state), os.ModePerm) 86 | require.NoError(t, err) 87 | } 88 | vc := New(srv.URL, path) 89 | _, _ = vc.Check("v0.1.2") 90 | require.EqualValues(t, tt.expectedRun, ran) 91 | 92 | b, err := os.ReadFile(path) 93 | require.NoError(t, err) 94 | if tt.expectedRun { 95 | require.NotEqualValues(t, tt.state, b) 96 | } else { 97 | require.EqualValues(t, tt.state, b) 98 | } 99 | }) 100 | } 101 | } 102 | 103 | func TestTemplate(t *testing.T) { 104 | 105 | for _, tt := range []struct { 106 | name string 107 | payload Payload 108 | exp string 109 | }{ 110 | { 111 | name: "empty", 112 | payload: Payload{}, 113 | exp: "", 114 | }, 115 | { 116 | name: "version with summary", 117 | payload: Payload{ 118 | Latest: &Latest{ 119 | Version: "v0.7.2", 120 | Summary: "A great version including amazing features.", 121 | Link: "https://atlasgo.io/v0.7.2", 122 | }, 123 | }, 124 | exp: `A new version of the Atlas Operator is available (v0.7.2): https://atlasgo.io/v0.7.2 125 | A great version including amazing features.`, 126 | }, 127 | { 128 | name: "version", 129 | payload: Payload{ 130 | Latest: &Latest{ 131 | Version: "v0.7.2", 132 | Link: "https://atlasgo.io/v0.7.2", 133 | }, 134 | }, 135 | exp: `A new version of the Atlas Operator is available (v0.7.2): https://atlasgo.io/v0.7.2`, 136 | }, 137 | { 138 | name: "with advisory", 139 | payload: Payload{ 140 | Advisory: &Advisory{Text: "This version contains a vulnerability, please upgrade."}, 141 | }, 142 | exp: `SECURITY ADVISORY 143 | This version contains a vulnerability, please upgrade.`, 144 | }, 145 | } { 146 | t.Run(tt.name, func(t *testing.T) { 147 | var b bytes.Buffer 148 | err := Notify.Execute(&b, tt.payload) 149 | require.NoError(t, err) 150 | require.EqualValues(t, tt.exp, b.String()) 151 | }) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /skaffold.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Atlas Operator Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: skaffold/v3 16 | kind: Config 17 | metadata: 18 | name: atlas-operator 19 | build: 20 | artifacts: 21 | - image: controller 22 | docker: 23 | dockerfile: Dockerfile 24 | buildArgs: 25 | OPERATOR_VERSION: v0.0.1-local-k8s 26 | profiles: 27 | - name: kustomize 28 | manifests: 29 | kustomize: 30 | paths: 31 | - config/default 32 | - config/sqlserver 33 | - config/customconfig 34 | - name: helm 35 | deploy: 36 | helm: 37 | releases: 38 | - name: atlas-operator 39 | chartPath: charts/atlas-operator 40 | namespace: atlas-operator-system 41 | createNamespace: true 42 | setValues: 43 | image: 44 | repository: controller 45 | tag: v0.0.1-local-k8s 46 | extraEnvs: 47 | - name: MSSQL_ACCEPT_EULA 48 | value: "Y" 49 | - name: MSSQL_PID 50 | value: "Developer" 51 | valuesFiles: 52 | - charts/atlas-operator/values.yaml 53 | -------------------------------------------------------------------------------- /test/e2e/testscript/migration-backoff-limit.txtar: -------------------------------------------------------------------------------- 1 | env DB_URL=mysql://root:pass@mysql.${NAMESPACE}:3306/myapp 2 | kubectl apply -f database.yaml 3 | kubectl create secret generic db-creds --from-literal=url=${DB_URL} 4 | 5 | # Wait for the first pod created 6 | kubectl-wait-available deploy/mysql 7 | # Wait for the DB ready before creating the schema 8 | kubectl-wait-ready -l app=mysql pods 9 | kubectl apply -f migration.yaml 10 | 11 | # Wait for the controller to detect the change 12 | exec sleep 20 13 | 14 | kubectl get -o jsonpath --template='{.status.failed}' AtlasMigration/mysql 15 | stdout 3 16 | 17 | # Create configmap to store the migrations directory 18 | kubectl create configmap migration-dir --from-file=migrations-v1 19 | kubectl patch -f migration.yaml --type merge --patch-file patch-target.yaml 20 | 21 | # Wait for the controller to detect the change 22 | exec sleep 10 23 | 24 | kubectl-wait-ready AtlasMigration/mysql 25 | kubectl get -o jsonpath --template='{.status.failed}' AtlasMigration/mysql 26 | stdout 0 27 | 28 | -- migrations-v1/atlas.sum -- 29 | h1:XBXbh+rzLis8gknjlIqnxXLBkOZ+sN2v2p7KjyVFYYM= 30 | 20230316085611.sql h1:br6W6LPEnnsejlz/7hRm9zthwStCzjN2vZkqVPxlmvo= 31 | 20230316090502.sql h1:GfeRjkSeoCt3JVRtLQNa/r50lRfpAPXS7AqTU2ZNFgY= 32 | -- migrations-v1/20230316085611.sql -- 33 | -- Create "users" table 34 | CREATE TABLE `users` ( 35 | `id` int NOT NULL, 36 | `user_name` varchar(255) NOT NULL, 37 | `email` varchar(255) NOT NULL, 38 | PRIMARY KEY (`id`) 39 | ) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci; 40 | -- migrations-v1/20230316090502.sql -- 41 | -- Create "posts" table 42 | CREATE TABLE `posts` ( 43 | `id` int NOT NULL, 44 | `user_id` int NOT NULL, 45 | `title` varchar(255) NOT NULL, 46 | `body` text NOT NULL, 47 | PRIMARY KEY (`id`), 48 | INDEX `user_id` (`user_id`), 49 | CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE 50 | ) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci; 51 | -- migration.yaml -- 52 | apiVersion: db.atlasgo.io/v1alpha1 53 | kind: AtlasMigration 54 | metadata: 55 | name: mysql 56 | spec: 57 | backoffLimit: 2 58 | dir: 59 | configMapRef: 60 | name: "migration-dir" 61 | -- patch-target.yaml -- 62 | spec: 63 | backoffLimit: 20 64 | urlFrom: 65 | secretKeyRef: 66 | name: db-creds 67 | key: url 68 | -- database.yaml -- 69 | apiVersion: v1 70 | kind: Service 71 | metadata: 72 | name: mysql 73 | spec: 74 | selector: 75 | app: mysql 76 | ports: 77 | - name: mysql 78 | port: 3306 79 | targetPort: mysql 80 | type: ClusterIP 81 | --- 82 | apiVersion: apps/v1 83 | kind: Deployment 84 | metadata: 85 | name: mysql 86 | spec: 87 | selector: 88 | matchLabels: 89 | app: mysql 90 | replicas: 1 91 | template: 92 | metadata: 93 | labels: 94 | app: mysql 95 | spec: 96 | containers: 97 | - name: mysql 98 | image: mysql:latest 99 | env: 100 | - name: MYSQL_ROOT_PASSWORD 101 | value: pass 102 | - name: MYSQL_DATABASE 103 | value: myapp 104 | ports: 105 | - containerPort: 3306 106 | name: mysql 107 | startupProbe: 108 | exec: 109 | command: [ "mysql", "-ppass", "-h", "127.0.0.1", "-e", "SELECT 1" ] 110 | failureThreshold: 30 111 | periodSeconds: 10 112 | -------------------------------------------------------------------------------- /test/e2e/testscript/migration-config-variables.txtar: -------------------------------------------------------------------------------- 1 | env MIGRATE_DB_URL=postgres://root:pass@postgres.${NAMESPACE}:5434/postgres?sslmode=disable 2 | env MIGRATE_DB_DEV_URL=postgres://root:pass@postgres.${NAMESPACE}:5435/postgres?sslmode=disable 3 | kubectl apply -f database.yaml 4 | kubectl create secret generic migrate-db-creds --from-literal=url=${MIGRATE_DB_URL} 5 | kubectl create configmap migrate-db-dev-creds --from-literal=url=${MIGRATE_DB_DEV_URL} 6 | # Create the secret to store ATLAS_TOKEN 7 | kubectl create secret generic atlas-token --from-literal=ATLAS_TOKEN=${ATLAS_TOKEN} 8 | 9 | # Wait for the first pod created 10 | kubectl-wait-available deploy/postgres 11 | # Wait for the DB ready before creating the schema 12 | kubectl-wait-ready -l app=postgres pods 13 | 14 | # Create the migration 15 | kubectl apply -f migration.yaml 16 | kubectl wait --for=jsonpath='{.status.conditions[*].reason}'=Applied --timeout=500s AtlasMigrations/sample 17 | 18 | # Patch a invalid config of schema from secret 19 | kubectl create secret generic migration-config --from-file=config.hcl 20 | kubectl patch -f migration.yaml --type merge --patch-file patch-migration-config-from-configmap.yaml 21 | kubectl wait --for=jsonpath='{.status.conditions[*].reason}'=Applied --timeout=500s AtlasMigrations/sample 22 | 23 | -- patch-migration-config-from-configmap.yaml -- 24 | spec: 25 | config: 26 | configFrom: 27 | secretKeyRef: 28 | name: migration-config 29 | key: config.hcl 30 | -- config.hcl -- 31 | variable "db_url" { 32 | type = string 33 | } 34 | variable "dev_db_url" { 35 | type = string 36 | } 37 | env "test" { 38 | url = var.db_url 39 | dev = var.dev_db_url 40 | } 41 | -- migration.yaml -- 42 | apiVersion: db.atlasgo.io/v1alpha1 43 | kind: AtlasMigration 44 | metadata: 45 | name: sample 46 | spec: 47 | envName: "test" 48 | vars: 49 | - key: "db_url" 50 | valueFrom: 51 | secretKeyRef: 52 | name: migrate-db-creds 53 | key: url 54 | - key: "dev_db_url" 55 | valueFrom: 56 | configMapKeyRef: 57 | name: migrate-db-dev-creds 58 | key: url 59 | cloud: 60 | tokenFrom: 61 | secretKeyRef: 62 | name: atlas-token 63 | key: ATLAS_TOKEN 64 | config: | 65 | variable "db_url" { 66 | type = string 67 | } 68 | variable "dev_db_url" { 69 | type = string 70 | } 71 | env "test" { 72 | url = var.db_url 73 | dev = var.dev_db_url 74 | } 75 | dir: 76 | local: 77 | 20230316085611.sql: | 78 | create sequence users_seq; 79 | create table users ( 80 | id int not null default nextval ('users_seq'), 81 | name varchar(255) not null, 82 | email varchar(255) unique not null, 83 | short_bio varchar(255) not null, 84 | primary key (id) 85 | ); 86 | atlas.sum: | 87 | h1:FwM0ApKo8xhcZFrSlpa6dYjvi0fnDPo/aZSzajtbHLc= 88 | 20230316085611.sql h1:ldFr73m6ZQzNi8q9dVJsOU/ZHmkBo4Sax03AaL0VUUs= 89 | -- database.yaml -- 90 | apiVersion: v1 91 | kind: Service 92 | metadata: 93 | name: postgres 94 | spec: 95 | selector: 96 | app: postgres 97 | ports: 98 | - name: pg-migrate 99 | port: 5434 100 | targetPort: pg-migrate 101 | - name: pg-migrate-dev 102 | port: 5435 103 | targetPort: pg-migrate-dev 104 | type: ClusterIP 105 | --- 106 | apiVersion: apps/v1 107 | kind: Deployment 108 | metadata: 109 | name: postgres 110 | spec: 111 | selector: 112 | matchLabels: 113 | app: postgres 114 | replicas: 1 115 | template: 116 | metadata: 117 | labels: 118 | app: postgres 119 | spec: 120 | securityContext: 121 | runAsNonRoot: true 122 | runAsUser: 999 123 | containers: 124 | - name: pg-migrate 125 | image: postgres:15.4 126 | securityContext: 127 | allowPrivilegeEscalation: false 128 | capabilities: 129 | drop: 130 | - all 131 | env: 132 | - name: POSTGRES_PASSWORD 133 | value: pass 134 | - name: POSTGRES_USER 135 | value: root 136 | - name: PGPORT 137 | value: "5434" 138 | ports: 139 | - containerPort: 5434 140 | name: pg-migrate 141 | startupProbe: 142 | exec: 143 | command: [ "pg_isready" ] 144 | failureThreshold: 30 145 | periodSeconds: 10 146 | - name: pg-migrate-dev 147 | image: postgres:15.4 148 | securityContext: 149 | allowPrivilegeEscalation: false 150 | capabilities: 151 | drop: 152 | - all 153 | env: 154 | - name: POSTGRES_PASSWORD 155 | value: pass 156 | - name: POSTGRES_USER 157 | value: root 158 | - name: PGPORT 159 | value: "5435" 160 | ports: 161 | - containerPort: 5435 162 | name: pg-migrate-dev 163 | startupProbe: 164 | exec: 165 | command: [ "pg_isready" ] 166 | failureThreshold: 30 167 | periodSeconds: 10 168 | -------------------------------------------------------------------------------- /test/e2e/testscript/migration-template-dir.txtar: -------------------------------------------------------------------------------- 1 | env MIGRATE_DB_URL=postgres://root:pass@postgres.${NAMESPACE}:5434/postgres?sslmode=disable 2 | env MIGRATE_DB_DEV_URL=postgres://root:pass@postgres.${NAMESPACE}:5435/postgres?sslmode=disable 3 | kubectl apply -f database.yaml 4 | kubectl create secret generic migrate-db-creds --from-literal=url=${MIGRATE_DB_URL} 5 | kubectl create configmap migrate-db-dev-creds --from-literal=url=${MIGRATE_DB_DEV_URL} 6 | # Create the secret to store ATLAS_TOKEN 7 | kubectl create secret generic atlas-token --from-literal=ATLAS_TOKEN=${ATLAS_TOKEN} 8 | 9 | # Wait for the first pod created 10 | kubectl-wait-available deploy/postgres 11 | # Wait for the DB ready before creating the schema 12 | kubectl-wait-ready -l app=postgres pods 13 | 14 | # Create the migration 15 | kubectl apply -f migration.yaml 16 | kubectl wait --for=jsonpath='{.status.conditions[*].reason}'=Applied --timeout=500s AtlasMigrations/sample 17 | 18 | -- config.hcl -- 19 | variable "db_url" { 20 | type = string 21 | } 22 | variable "dev_db_url" { 23 | type = string 24 | } 25 | env "test" { 26 | url = var.db_url 27 | dev = var.dev_db_url 28 | } 29 | -- migration.yaml -- 30 | apiVersion: db.atlasgo.io/v1alpha1 31 | kind: AtlasMigration 32 | metadata: 33 | name: sample 34 | spec: 35 | envName: "test" 36 | vars: 37 | - key: "db_url" 38 | valueFrom: 39 | secretKeyRef: 40 | name: migrate-db-creds 41 | key: url 42 | - key: "dev_db_url" 43 | valueFrom: 44 | configMapKeyRef: 45 | name: migrate-db-dev-creds 46 | key: url 47 | cloud: 48 | tokenFrom: 49 | secretKeyRef: 50 | name: atlas-token 51 | key: ATLAS_TOKEN 52 | config: | 53 | variable "db_url" { 54 | type = string 55 | } 56 | variable "dev_db_url" { 57 | type = string 58 | } 59 | data "template_dir" "migrations" { 60 | path = "migrations" 61 | vars = { 62 | schema = "public" 63 | } 64 | } 65 | env "test" { 66 | url = var.db_url 67 | dev = var.dev_db_url 68 | migration { 69 | dir = data.template_dir.migrations.url 70 | } 71 | } 72 | dir: 73 | local: 74 | 20230316085611.sql: | 75 | create sequence users_seq; 76 | create table {{ .schema }}.users ( 77 | id int not null default nextval ('users_seq'), 78 | name varchar(255) not null, 79 | email varchar(255) unique not null, 80 | short_bio varchar(255) not null, 81 | primary key (id) 82 | ); 83 | atlas.sum: | 84 | h1:FwM0ApKo8xhcZFrSlpa6dYjvi0fnDPo/aZSzajtbHLc= 85 | 20230316085611.sql h1:ldFr73m6ZQzNi8q9dVJsOU/ZHmkBo4Sax03AaL0VUUs= 86 | -- database.yaml -- 87 | apiVersion: v1 88 | kind: Service 89 | metadata: 90 | name: postgres 91 | spec: 92 | selector: 93 | app: postgres 94 | ports: 95 | - name: pg-migrate 96 | port: 5434 97 | targetPort: pg-migrate 98 | - name: pg-migrate-dev 99 | port: 5435 100 | targetPort: pg-migrate-dev 101 | type: ClusterIP 102 | --- 103 | apiVersion: apps/v1 104 | kind: Deployment 105 | metadata: 106 | name: postgres 107 | spec: 108 | selector: 109 | matchLabels: 110 | app: postgres 111 | replicas: 1 112 | template: 113 | metadata: 114 | labels: 115 | app: postgres 116 | spec: 117 | securityContext: 118 | runAsNonRoot: true 119 | runAsUser: 999 120 | containers: 121 | - name: pg-migrate 122 | image: postgres:15.4 123 | securityContext: 124 | allowPrivilegeEscalation: false 125 | capabilities: 126 | drop: 127 | - all 128 | env: 129 | - name: POSTGRES_PASSWORD 130 | value: pass 131 | - name: POSTGRES_USER 132 | value: root 133 | - name: PGPORT 134 | value: "5434" 135 | ports: 136 | - containerPort: 5434 137 | name: pg-migrate 138 | startupProbe: 139 | exec: 140 | command: [ "pg_isready" ] 141 | failureThreshold: 30 142 | periodSeconds: 10 143 | - name: pg-migrate-dev 144 | image: postgres:15.4 145 | securityContext: 146 | allowPrivilegeEscalation: false 147 | capabilities: 148 | drop: 149 | - all 150 | env: 151 | - name: POSTGRES_PASSWORD 152 | value: pass 153 | - name: POSTGRES_USER 154 | value: root 155 | - name: PGPORT 156 | value: "5435" 157 | ports: 158 | - containerPort: 5435 159 | name: pg-migrate-dev 160 | startupProbe: 161 | exec: 162 | command: [ "pg_isready" ] 163 | failureThreshold: 30 164 | periodSeconds: 10 165 | -------------------------------------------------------------------------------- /test/e2e/testscript/schema-backoff-limit.txtar: -------------------------------------------------------------------------------- 1 | env DB_URL=mysql://root:pass@mysql.${NAMESPACE}:3306/myapp 2 | kubectl apply -f database.yaml 3 | kubectl create secret generic db-creds --from-literal=url=${DB_URL} 4 | 5 | # Wait for the first pod created 6 | kubectl-wait-available deploy/mysql 7 | # Wait for the DB ready before creating the schema 8 | kubectl-wait-ready -l app=mysql pods 9 | 10 | # Create the schema 11 | kubectl apply -f schema.yaml 12 | 13 | # Wait for the controller to detect the change 14 | exec sleep 20 15 | 16 | kubectl get -o jsonpath --template='{.status.failed}' AtlasSchema/mysql 17 | stdout 3 18 | 19 | kubectl patch -f schema.yaml --type merge --patch-file patch-target.yaml 20 | 21 | # Wait for the controller to detect the change 22 | exec sleep 10 23 | 24 | kubectl-wait-ready AtlasSchema/mysql 25 | kubectl get -o jsonpath --template='{.status.failed}' AtlasSchema/mysql 26 | stdout 0 27 | 28 | -- schema.yaml -- 29 | apiVersion: db.atlasgo.io/v1alpha1 30 | kind: AtlasSchema 31 | metadata: 32 | name: mysql 33 | spec: 34 | backoffLimit: 5 35 | schema: 36 | sql: | 37 | create table users ( 38 | id int not null auto_increment, 39 | name varchar(255) not null, 40 | email varchar(255) unique not null, 41 | short_bio varchar(255) not null, 42 | primary key (id) 43 | ); 44 | -- patch-target.yaml -- 45 | spec: 46 | backoffLimit: 2 47 | urlFrom: 48 | secretKeyRef: 49 | name: db-creds 50 | key: url 51 | -- database.yaml -- 52 | apiVersion: v1 53 | kind: Service 54 | metadata: 55 | name: mysql 56 | spec: 57 | selector: 58 | app: mysql 59 | ports: 60 | - name: mysql 61 | port: 3306 62 | targetPort: mysql 63 | type: ClusterIP 64 | --- 65 | apiVersion: apps/v1 66 | kind: Deployment 67 | metadata: 68 | name: mysql 69 | spec: 70 | selector: 71 | matchLabels: 72 | app: mysql 73 | replicas: 1 74 | template: 75 | metadata: 76 | labels: 77 | app: mysql 78 | spec: 79 | containers: 80 | - name: mysql 81 | image: mysql:latest 82 | env: 83 | - name: MYSQL_ROOT_PASSWORD 84 | value: pass 85 | - name: MYSQL_DATABASE 86 | value: myapp 87 | ports: 88 | - containerPort: 3306 89 | name: mysql 90 | startupProbe: 91 | exec: 92 | command: [ "mysql", "-ppass", "-h", "127.0.0.1", "-e", "SELECT 1" ] 93 | failureThreshold: 30 94 | periodSeconds: 10 95 | -------------------------------------------------------------------------------- /test/e2e/testscript/schema-clickhouse.txtar: -------------------------------------------------------------------------------- 1 | env DB_URL=clickhouse://root:pass@clickhouse.${NAMESPACE}:9000/myapp 2 | kubectl apply -f database.yaml 3 | kubectl create secret generic db-creds --from-literal=url=${DB_URL} 4 | 5 | # Wait for the first pod created 6 | kubectl-wait-available deploy/clickhouse 7 | # Wait for the DB ready before creating the schema 8 | kubectl-wait-ready -l app=clickhouse pods 9 | 10 | # Create the secret to store ATLAS_TOKEN 11 | kubectl create secret generic atlas-token --from-literal=ATLAS_TOKEN=${ATLAS_TOKEN} 12 | 13 | # Create the configmap to store the schema.sql 14 | kubectl create configmap clickhouse-schema --from-file=./schema-v1 --dry-run=client -o yaml 15 | stdin stdout 16 | kubectl apply -f - 17 | 18 | # Create the schema 19 | kubectl apply -f schema.yaml 20 | kubectl-wait-ready AtlasSchema/clickhouse 21 | 22 | # Inspect the schema to ensure it's correct 23 | atlas schema inspect -u ${DB_URL} 24 | cmp stdout schema-v1.hcl 25 | -- schema-v1.hcl -- 26 | table "users" { 27 | schema = schema.myapp 28 | engine = MergeTree 29 | settings = { 30 | index_granularity = 8192 31 | } 32 | column "id" { 33 | null = false 34 | type = UInt32 35 | } 36 | column "name" { 37 | null = false 38 | type = String 39 | } 40 | column "created" { 41 | null = false 42 | type = DateTime 43 | } 44 | primary_key { 45 | columns = [column.id] 46 | } 47 | sort { 48 | columns = [column.id] 49 | } 50 | } 51 | schema "myapp" { 52 | engine = Atomic 53 | } 54 | -- schema-v1/schema.sql -- 55 | CREATE TABLE `users` ( 56 | `id` UInt32, 57 | `name` String, 58 | `created` DateTime 59 | ) 60 | ENGINE = MergeTree 61 | PRIMARY KEY (`id`) 62 | SETTINGS index_granularity = 8192; 63 | -- schema.yaml -- 64 | apiVersion: db.atlasgo.io/v1alpha1 65 | kind: AtlasSchema 66 | metadata: 67 | name: clickhouse 68 | spec: 69 | urlFrom: 70 | secretKeyRef: 71 | name: db-creds 72 | key: url 73 | policy: 74 | lint: 75 | destructive: 76 | error: true 77 | diff: 78 | skip: 79 | drop_column: true 80 | cloud: 81 | tokenFrom: 82 | secretKeyRef: 83 | name: atlas-token 84 | key: ATLAS_TOKEN 85 | schema: 86 | configMapKeyRef: 87 | key: schema.sql 88 | name: clickhouse-schema 89 | exclude: 90 | - ignore_me 91 | -- database.yaml -- 92 | apiVersion: v1 93 | kind: Service 94 | metadata: 95 | name: clickhouse 96 | spec: 97 | selector: 98 | app: clickhouse 99 | ports: 100 | - name: clickhouse 101 | port: 9000 102 | targetPort: clickhouse 103 | type: ClusterIP 104 | --- 105 | apiVersion: apps/v1 106 | kind: Deployment 107 | metadata: 108 | name: clickhouse 109 | spec: 110 | selector: 111 | matchLabels: 112 | app: clickhouse 113 | replicas: 1 114 | template: 115 | metadata: 116 | labels: 117 | app: clickhouse 118 | spec: 119 | containers: 120 | - name: clickhouse 121 | image: clickhouse/clickhouse-server:latest 122 | env: 123 | - name: CLICKHOUSE_USER 124 | value: root 125 | - name: CLICKHOUSE_PASSWORD 126 | value: pass 127 | - name: CLICKHOUSE_DB 128 | value: myapp 129 | ports: 130 | - containerPort: 9000 131 | name: clickhouse 132 | startupProbe: 133 | exec: 134 | command: [ "clickhouse-client", "-q", "SELECT 1" ] 135 | failureThreshold: 30 136 | periodSeconds: 10 137 | -------------------------------------------------------------------------------- /test/e2e/testscript/schema-composite.txtar: -------------------------------------------------------------------------------- 1 | env SCHEMA_DB_URL=postgres://root:pass@postgres.${NAMESPACE}:5432/postgres?sslmode=disable 2 | env SCHEMA_DB_DEV_URL=postgres://root:pass@postgres.${NAMESPACE}:5433/postgres?sslmode=disable 3 | kubectl apply -f database.yaml 4 | kubectl create secret generic schema-db-creds --from-literal=url=${SCHEMA_DB_URL} 5 | kubectl create configmap schema-db-dev-creds --from-literal=url=${SCHEMA_DB_DEV_URL} 6 | # Create the secret to store ATLAS_TOKEN 7 | kubectl create secret generic atlas-token --from-literal=ATLAS_TOKEN=${ATLAS_TOKEN} 8 | 9 | # Wait for the first pod created 10 | kubectl-wait-available deploy/postgres 11 | # Wait for the DB ready before creating the schema 12 | kubectl-wait-ready -l app=postgres pods 13 | 14 | # Create the schema 15 | kubectl apply -f schema.yaml 16 | kubectl wait --for=jsonpath='{.status.conditions[*].reason}'=Applied --timeout=500s AtlasSchemas/sample 17 | 18 | atlas schema inspect -u ${SCHEMA_DB_URL} 19 | cmp stdout schema.hcl 20 | 21 | -- schema.yaml -- 22 | apiVersion: db.atlasgo.io/v1alpha1 23 | kind: AtlasSchema 24 | metadata: 25 | name: sample 26 | spec: 27 | envName: "test" 28 | cloud: 29 | repo: atlas-operator 30 | tokenFrom: 31 | secretKeyRef: 32 | name: atlas-token 33 | key: ATLAS_TOKEN 34 | vars: 35 | - key: "db_url" 36 | valueFrom: 37 | secretKeyRef: 38 | name: schema-db-creds 39 | key: url 40 | - key: "dev_db_url" 41 | valueFrom: 42 | configMapKeyRef: 43 | name: schema-db-dev-creds 44 | key: url 45 | config: | 46 | variable "db_url" { 47 | type = string 48 | } 49 | variable "dev_db_url" { 50 | type = string 51 | } 52 | data "external_schema" "users" { 53 | program = ["echo", "CREATE TABLE users (id int not null, name varchar(255) not null, email varchar(255) not null, short_bio varchar(255) not null, PRIMARY KEY (id));"] 54 | } 55 | data "external_schema" "posts" { 56 | program = ["echo", "CREATE TABLE posts (id int not null, title varchar(255) not null, body text not null, PRIMARY KEY (id));"] 57 | } 58 | data "composite_schema" "app" { 59 | schema "public" { 60 | url = data.external_schema.users.url 61 | } 62 | schema "public" { 63 | url = data.external_schema.posts.url 64 | } 65 | } 66 | env "test" { 67 | schema { 68 | src = data.composite_schema.app.url 69 | } 70 | url = var.db_url 71 | dev = var.dev_db_url 72 | } 73 | -- schema.hcl -- 74 | table "posts" { 75 | schema = schema.public 76 | column "id" { 77 | null = false 78 | type = integer 79 | } 80 | column "title" { 81 | null = false 82 | type = character_varying(255) 83 | } 84 | column "body" { 85 | null = false 86 | type = text 87 | } 88 | primary_key { 89 | columns = [column.id] 90 | } 91 | } 92 | table "users" { 93 | schema = schema.public 94 | column "id" { 95 | null = false 96 | type = integer 97 | } 98 | column "name" { 99 | null = false 100 | type = character_varying(255) 101 | } 102 | column "email" { 103 | null = false 104 | type = character_varying(255) 105 | } 106 | column "short_bio" { 107 | null = false 108 | type = character_varying(255) 109 | } 110 | primary_key { 111 | columns = [column.id] 112 | } 113 | } 114 | schema "public" { 115 | comment = "standard public schema" 116 | } 117 | -- database.yaml -- 118 | apiVersion: v1 119 | kind: Service 120 | metadata: 121 | name: postgres 122 | spec: 123 | selector: 124 | app: postgres 125 | ports: 126 | - name: postgres 127 | port: 5432 128 | targetPort: postgres 129 | - name: postgres-dev 130 | port: 5433 131 | targetPort: postgres-dev 132 | type: ClusterIP 133 | --- 134 | apiVersion: apps/v1 135 | kind: Deployment 136 | metadata: 137 | name: postgres 138 | spec: 139 | selector: 140 | matchLabels: 141 | app: postgres 142 | replicas: 1 143 | template: 144 | metadata: 145 | labels: 146 | app: postgres 147 | spec: 148 | securityContext: 149 | runAsNonRoot: true 150 | runAsUser: 999 151 | containers: 152 | - name: postgres 153 | image: postgres:15.4 154 | securityContext: 155 | allowPrivilegeEscalation: false 156 | capabilities: 157 | drop: 158 | - all 159 | env: 160 | - name: POSTGRES_PASSWORD 161 | value: pass 162 | - name: POSTGRES_USER 163 | value: root 164 | ports: 165 | - containerPort: 5432 166 | name: postgres 167 | startupProbe: 168 | exec: 169 | command: [ "pg_isready" ] 170 | failureThreshold: 30 171 | periodSeconds: 10 172 | - name: postgres-dev 173 | image: postgres:15.4 174 | securityContext: 175 | allowPrivilegeEscalation: false 176 | capabilities: 177 | drop: 178 | - all 179 | env: 180 | - name: POSTGRES_PASSWORD 181 | value: pass 182 | - name: POSTGRES_USER 183 | value: root 184 | - name: PGPORT 185 | value: "5433" 186 | ports: 187 | - containerPort: 5433 188 | name: postgres-dev 189 | startupProbe: 190 | exec: 191 | command: [ "pg_isready" ] 192 | failureThreshold: 30 193 | periodSeconds: 10 194 | -------------------------------------------------------------------------------- /test/e2e/testscript/schema-config-lint-destructive.txtar: -------------------------------------------------------------------------------- 1 | env DB_URL=mysql://root:pass@mysql.${NAMESPACE}:3306/myapp 2 | kubectl apply -f database.yaml 3 | kubectl create secret generic db-creds --from-literal=url=${DB_URL} 4 | kubectl create secret generic atlas-token --from-literal=ATLAS_TOKEN=${ATLAS_TOKEN} 5 | 6 | # Wait for the first pod created 7 | kubectl-wait-available deploy/mysql 8 | # Wait for the DB ready before creating the schema 9 | kubectl-wait-ready -l app=mysql pods 10 | 11 | # Create the schema 12 | kubectl apply -f schema.yaml 13 | kubectl-wait-ready AtlasSchema/mysql 14 | 15 | # Inspect the schema to ensure it's correct 16 | atlas schema inspect -u ${DB_URL} 17 | cmp stdout schema.hcl 18 | 19 | kubectl patch -f schema.yaml --type merge --patch-file patch-remove-bio.yaml 20 | 21 | # Wait for the controller to detect the change 22 | exec sleep 10 23 | 24 | # Ensure the controller is aware of the change 25 | kubectl wait --for=jsonpath='{.status.conditions[*].reason}'=ApplyingSchema --timeout=500s AtlasSchemas/mysql 26 | # Check the error message 27 | kubectl get AtlasSchema/mysql -o jsonpath --template='{.status.conditions[*].message}' 28 | stdout 'Rejected by review policy: errors or warnings were found' 29 | 30 | # Disable the lint policy error via custom config 31 | kubectl patch -f schema.yaml --type merge --patch-file patch-disable-lint-destructive.yaml 32 | # Wait for the controller to detect the change 33 | exec sleep 10 34 | kubectl wait --for=jsonpath='{.status.conditions[*].reason}'=Applied --timeout=500s AtlasSchemas/mysql 35 | 36 | -- schema.hcl -- 37 | table "users" { 38 | schema = schema.myapp 39 | column "id" { 40 | null = false 41 | type = int 42 | auto_increment = true 43 | } 44 | column "name" { 45 | null = false 46 | type = varchar(255) 47 | } 48 | column "email" { 49 | null = false 50 | type = varchar(255) 51 | } 52 | column "short_bio" { 53 | null = false 54 | type = varchar(255) 55 | } 56 | primary_key { 57 | columns = [column.id] 58 | } 59 | index "email" { 60 | unique = true 61 | columns = [column.email] 62 | } 63 | } 64 | schema "myapp" { 65 | charset = "utf8mb4" 66 | collate = "utf8mb4_0900_ai_ci" 67 | } 68 | -- patch-remove-bio.yaml -- 69 | spec: 70 | schema: 71 | sql: | 72 | create table users ( 73 | id int not null auto_increment, 74 | name varchar(255) not null, 75 | email varchar(255) unique not null, 76 | primary key (id) 77 | ); 78 | -- patch-disable-lint-destructive.yaml -- 79 | spec: 80 | envName: "test" 81 | config: | 82 | env "test" { 83 | lint { 84 | destructive { 85 | error = false 86 | } 87 | } 88 | } 89 | -- schema.yaml -- 90 | apiVersion: db.atlasgo.io/v1alpha1 91 | kind: AtlasSchema 92 | metadata: 93 | name: mysql 94 | spec: 95 | urlFrom: 96 | secretKeyRef: 97 | name: db-creds 98 | key: url 99 | cloud: 100 | tokenFrom: 101 | secretKeyRef: 102 | name: atlas-token 103 | key: ATLAS_TOKEN 104 | schema: 105 | sql: | 106 | create table users ( 107 | id int not null auto_increment, 108 | name varchar(255) not null, 109 | email varchar(255) unique not null, 110 | short_bio varchar(255) not null, 111 | primary key (id) 112 | ); 113 | -- database.yaml -- 114 | apiVersion: v1 115 | kind: Service 116 | metadata: 117 | name: mysql 118 | spec: 119 | selector: 120 | app: mysql 121 | ports: 122 | - name: mysql 123 | port: 3306 124 | targetPort: mysql 125 | type: ClusterIP 126 | --- 127 | apiVersion: apps/v1 128 | kind: Deployment 129 | metadata: 130 | name: mysql 131 | spec: 132 | selector: 133 | matchLabels: 134 | app: mysql 135 | replicas: 1 136 | template: 137 | metadata: 138 | labels: 139 | app: mysql 140 | spec: 141 | containers: 142 | - name: mysql 143 | image: mysql:latest 144 | env: 145 | - name: MYSQL_ROOT_PASSWORD 146 | value: pass 147 | - name: MYSQL_DATABASE 148 | value: myapp 149 | ports: 150 | - containerPort: 3306 151 | name: mysql 152 | startupProbe: 153 | exec: 154 | command: [ "mysql", "-ppass", "-h", "127.0.0.1", "-e", "SELECT 1" ] 155 | failureThreshold: 30 156 | periodSeconds: 10 157 | -------------------------------------------------------------------------------- /test/e2e/testscript/schema-config-variables.txtar: -------------------------------------------------------------------------------- 1 | env SCHEMA_DB_URL=postgres://root:pass@postgres.${NAMESPACE}:5432/postgres?sslmode=disable 2 | env SCHEMA_DB_DEV_URL=postgres://root:pass@postgres.${NAMESPACE}:5433/postgres?sslmode=disable 3 | kubectl apply -f database.yaml 4 | kubectl create secret generic schema-db-creds --from-literal=url=${SCHEMA_DB_URL} 5 | kubectl create configmap schema-db-dev-creds --from-literal=url=${SCHEMA_DB_DEV_URL} 6 | # Create the secret to store ATLAS_TOKEN 7 | kubectl create secret generic atlas-token --from-literal=ATLAS_TOKEN=${ATLAS_TOKEN} 8 | 9 | # Wait for the first pod created 10 | kubectl-wait-available deploy/postgres 11 | # Wait for the DB ready before creating the schema 12 | kubectl-wait-ready -l app=postgres pods 13 | 14 | # Create the schema 15 | kubectl apply -f schema.yaml 16 | kubectl wait --for=jsonpath='{.status.conditions[*].reason}'=Applied --timeout=500s AtlasSchemas/sample 17 | 18 | # Patch a invalid config of schema from secret 19 | kubectl create secret generic schema-config --from-file=config.hcl 20 | kubectl patch -f schema.yaml --type merge --patch-file patch-schema-config-from-configmap.yaml 21 | kubectl wait --for=jsonpath='{.status.conditions[*].reason}'=Applied --timeout=500s AtlasSchemas/sample 22 | 23 | -- schema.yaml -- 24 | apiVersion: db.atlasgo.io/v1alpha1 25 | kind: AtlasSchema 26 | metadata: 27 | name: sample 28 | spec: 29 | envName: "test" 30 | schema: 31 | sql: | 32 | create table users ( 33 | id int not null, 34 | name varchar(255) not null, 35 | email varchar(255) unique not null, 36 | short_bio varchar(255) not null, 37 | primary key (id) 38 | ); 39 | cloud: 40 | repo: atlas-operator 41 | tokenFrom: 42 | secretKeyRef: 43 | name: atlas-token 44 | key: ATLAS_TOKEN 45 | vars: 46 | - key: "db_url" 47 | valueFrom: 48 | secretKeyRef: 49 | name: schema-db-creds 50 | key: url 51 | - key: "dev_db_url" 52 | valueFrom: 53 | configMapKeyRef: 54 | name: schema-db-dev-creds 55 | key: url 56 | config: | 57 | variable "db_url" { 58 | type = string 59 | } 60 | variable "dev_db_url" { 61 | type = string 62 | } 63 | env "test" { 64 | url = var.db_url 65 | dev = var.dev_db_url 66 | } 67 | -- patch-schema-config-from-configmap.yaml -- 68 | spec: 69 | config: 70 | configFrom: 71 | secretKeyRef: 72 | name: schema-config 73 | key: config.hcl 74 | -- config.hcl -- 75 | variable "db_url" { 76 | type = string 77 | } 78 | variable "dev_db_url" { 79 | type = string 80 | } 81 | env "test" { 82 | url = var.db_url 83 | dev = var.dev_db_url 84 | } 85 | -- database.yaml -- 86 | apiVersion: v1 87 | kind: Service 88 | metadata: 89 | name: postgres 90 | spec: 91 | selector: 92 | app: postgres 93 | ports: 94 | - name: postgres 95 | port: 5432 96 | targetPort: postgres 97 | - name: postgres-dev 98 | port: 5433 99 | targetPort: postgres-dev 100 | type: ClusterIP 101 | --- 102 | apiVersion: apps/v1 103 | kind: Deployment 104 | metadata: 105 | name: postgres 106 | spec: 107 | selector: 108 | matchLabels: 109 | app: postgres 110 | replicas: 1 111 | template: 112 | metadata: 113 | labels: 114 | app: postgres 115 | spec: 116 | securityContext: 117 | runAsNonRoot: true 118 | runAsUser: 999 119 | containers: 120 | - name: postgres 121 | image: postgres:15.4 122 | securityContext: 123 | allowPrivilegeEscalation: false 124 | capabilities: 125 | drop: 126 | - all 127 | env: 128 | - name: POSTGRES_PASSWORD 129 | value: pass 130 | - name: POSTGRES_USER 131 | value: root 132 | ports: 133 | - containerPort: 5432 134 | name: postgres 135 | startupProbe: 136 | exec: 137 | command: [ "pg_isready" ] 138 | failureThreshold: 30 139 | periodSeconds: 10 140 | - name: postgres-dev 141 | image: postgres:15.4 142 | securityContext: 143 | allowPrivilegeEscalation: false 144 | capabilities: 145 | drop: 146 | - all 147 | env: 148 | - name: POSTGRES_PASSWORD 149 | value: pass 150 | - name: POSTGRES_USER 151 | value: root 152 | - name: PGPORT 153 | value: "5433" 154 | ports: 155 | - containerPort: 5433 156 | name: postgres-dev 157 | startupProbe: 158 | exec: 159 | command: [ "pg_isready" ] 160 | failureThreshold: 30 161 | periodSeconds: 10 162 | -------------------------------------------------------------------------------- /test/e2e/testscript/schema-hash-configmap.txtar: -------------------------------------------------------------------------------- 1 | env DB_URL=mysql://root:pass@mysql.${NAMESPACE}:3306/myapp 2 | kubectl apply -f database.yaml 3 | kubectl create secret generic db-creds --from-literal=url=${DB_URL} 4 | 5 | # Wait for the first pod created 6 | kubectl-wait-available deploy/mysql 7 | # Wait for the DB ready before creating the schema 8 | kubectl-wait-ready -l app=mysql pods 9 | 10 | # Create the configmap to store the schema.sql 11 | kubectl create configmap mysql-schema --from-file=./schema-v1 --dry-run=client -o yaml 12 | stdin stdout 13 | kubectl apply -f - 14 | 15 | # Create the schema 16 | kubectl apply -f schema.yaml 17 | kubectl-wait-ready AtlasSchema/mysql 18 | 19 | kubectl get -o jsonpath --template='{.status.observed_hash}' AtlasSchema/mysql 20 | stdout oAoRLC2AXyGha6pKDollSqBB5ovjB\+qK78aAN9dkOow\= 21 | 22 | # Update the configmap with the new schema 23 | kubectl create configmap mysql-schema --from-file=./schema-v2 --dry-run=client -o yaml 24 | stdin stdout 25 | kubectl apply -f - 26 | 27 | # Ensure the controller is aware of the change 28 | kubectl wait --for=condition=ready=false --timeout=500s AtlasSchema/mysql 29 | kubectl-wait-ready AtlasSchema/mysql 30 | 31 | # Hash should be updated 32 | kubectl get -o jsonpath --template='{.status.observed_hash}' AtlasSchema/mysql 33 | stdout mlBKa2H4Mt7J8uStzuFj6Ps0XRIB9z3EuZPfOw6BeIA\= 34 | 35 | -- schema-v1/schema.sql -- 36 | create table users ( 37 | id int not null auto_increment, 38 | name varchar(255) not null, 39 | email varchar(255) unique not null, 40 | short_bio varchar(255) not null, 41 | primary key (id) 42 | ); 43 | -- schema-v2/schema.sql -- 44 | create table users ( 45 | id int not null auto_increment, 46 | name varchar(255) not null, 47 | email varchar(255) unique not null, 48 | short_bio varchar(255) not null, 49 | phone varchar(255) not null, 50 | primary key (id) 51 | ); 52 | -- schema.yaml -- 53 | apiVersion: db.atlasgo.io/v1alpha1 54 | kind: AtlasSchema 55 | metadata: 56 | name: mysql 57 | spec: 58 | urlFrom: 59 | secretKeyRef: 60 | name: db-creds 61 | key: url 62 | schema: 63 | configMapKeyRef: 64 | key: schema.sql 65 | name: mysql-schema 66 | -- database.yaml -- 67 | apiVersion: v1 68 | kind: Service 69 | metadata: 70 | name: mysql 71 | spec: 72 | selector: 73 | app: mysql 74 | ports: 75 | - name: mysql 76 | port: 3306 77 | targetPort: mysql 78 | - name: mysql-dev 79 | port: 3307 80 | targetPort: mysql-dev 81 | type: ClusterIP 82 | --- 83 | apiVersion: apps/v1 84 | kind: Deployment 85 | metadata: 86 | name: mysql 87 | spec: 88 | selector: 89 | matchLabels: 90 | app: mysql 91 | replicas: 1 92 | template: 93 | metadata: 94 | labels: 95 | app: mysql 96 | spec: 97 | containers: 98 | - name: mysql 99 | image: mysql:latest 100 | env: 101 | - name: MYSQL_ROOT_PASSWORD 102 | value: pass 103 | - name: MYSQL_DATABASE 104 | value: myapp 105 | ports: 106 | - containerPort: 3306 107 | name: mysql 108 | startupProbe: 109 | exec: 110 | command: [ "mysql", "-ppass", "-h", "127.0.0.1", "-e", "SELECT 1" ] 111 | failureThreshold: 30 112 | periodSeconds: 10 113 | - name: mysql-dev 114 | image: mysql:latest 115 | env: 116 | - name: MYSQL_ROOT_PASSWORD 117 | value: pass 118 | - name: MYSQL_DATABASE 119 | value: myapp 120 | - name: MYSQL_TCP_PORT 121 | value: "3307" 122 | ports: 123 | - containerPort: 3307 124 | name: mysql-dev 125 | startupProbe: 126 | exec: 127 | command: [ "mysql", "-ppass", "-h", "127.0.0.1", "-e", "SELECT 1" ] 128 | failureThreshold: 30 129 | periodSeconds: 10 130 | -------------------------------------------------------------------------------- /test/e2e/testscript/schema-hash.txtar: -------------------------------------------------------------------------------- 1 | env DB_URL=mysql://root:pass@mysql.${NAMESPACE}:3306/myapp 2 | kubectl apply -f database.yaml 3 | kubectl create secret generic db-creds --from-literal=url=${DB_URL} 4 | 5 | # Wait for the first pod created 6 | kubectl-wait-available deploy/mysql 7 | # Wait for the DB ready before creating the schema 8 | kubectl-wait-ready -l app=mysql pods 9 | 10 | # Create the schema 11 | kubectl apply -f schema.yaml 12 | kubectl-wait-ready AtlasSchema/mysql 13 | 14 | kubectl get -o jsonpath --template='{.status.observed_hash}' AtlasSchema/mysql 15 | stdout oAoRLC2AXyGha6pKDollSqBB5ovjB\+qK78aAN9dkOow\= 16 | 17 | # Update the configmap with the new schema 18 | kubectl patch -f schema.yaml --type merge --patch-file schema-v2-patch.yaml 19 | 20 | # Ensure the controller is aware of the change 21 | kubectl wait --for=condition=ready=false --timeout=500s AtlasSchema/mysql 22 | kubectl-wait-ready AtlasSchema/mysql 23 | 24 | # Hash should be updated 25 | kubectl get -o jsonpath --template='{.status.observed_hash}' AtlasSchema/mysql 26 | stdout mlBKa2H4Mt7J8uStzuFj6Ps0XRIB9z3EuZPfOw6BeIA\= 27 | 28 | -- schema-v1/schema.sql -- 29 | create table users ( 30 | id int not null auto_increment, 31 | name varchar(255) not null, 32 | email varchar(255) unique not null, 33 | short_bio varchar(255) not null, 34 | primary key (id) 35 | ); 36 | -- schema-v2/schema.sql -- 37 | create table users ( 38 | id int not null auto_increment, 39 | name varchar(255) not null, 40 | email varchar(255) unique not null, 41 | short_bio varchar(255) not null, 42 | phone varchar(255) not null, 43 | primary key (id) 44 | ); 45 | -- schema.yaml -- 46 | apiVersion: db.atlasgo.io/v1alpha1 47 | kind: AtlasSchema 48 | metadata: 49 | name: mysql 50 | spec: 51 | urlFrom: 52 | secretKeyRef: 53 | name: db-creds 54 | key: url 55 | schema: 56 | sql: | 57 | create table users ( 58 | id int not null auto_increment, 59 | name varchar(255) not null, 60 | email varchar(255) unique not null, 61 | short_bio varchar(255) not null, 62 | primary key (id) 63 | ); 64 | -- schema-v2-patch.yaml -- 65 | spec: 66 | schema: 67 | sql: | 68 | create table users ( 69 | id int not null auto_increment, 70 | name varchar(255) not null, 71 | email varchar(255) unique not null, 72 | short_bio varchar(255) not null, 73 | phone varchar(255) not null, 74 | primary key (id) 75 | ); 76 | -- database.yaml -- 77 | apiVersion: v1 78 | kind: Service 79 | metadata: 80 | name: mysql 81 | spec: 82 | selector: 83 | app: mysql 84 | ports: 85 | - name: mysql 86 | port: 3306 87 | targetPort: mysql 88 | - name: mysql-dev 89 | port: 3307 90 | targetPort: mysql-dev 91 | type: ClusterIP 92 | --- 93 | apiVersion: apps/v1 94 | kind: Deployment 95 | metadata: 96 | name: mysql 97 | spec: 98 | selector: 99 | matchLabels: 100 | app: mysql 101 | replicas: 1 102 | template: 103 | metadata: 104 | labels: 105 | app: mysql 106 | spec: 107 | containers: 108 | - name: mysql 109 | image: mysql:latest 110 | env: 111 | - name: MYSQL_ROOT_PASSWORD 112 | value: pass 113 | - name: MYSQL_DATABASE 114 | value: myapp 115 | ports: 116 | - containerPort: 3306 117 | name: mysql 118 | startupProbe: 119 | exec: 120 | command: [ "mysql", "-ppass", "-h", "127.0.0.1", "-e", "SELECT 1" ] 121 | failureThreshold: 30 122 | periodSeconds: 10 123 | - name: mysql-dev 124 | image: mysql:latest 125 | env: 126 | - name: MYSQL_ROOT_PASSWORD 127 | value: pass 128 | - name: MYSQL_DATABASE 129 | value: myapp 130 | - name: MYSQL_TCP_PORT 131 | value: "3307" 132 | ports: 133 | - containerPort: 3307 134 | name: mysql-dev 135 | startupProbe: 136 | exec: 137 | command: [ "mysql", "-ppass", "-h", "127.0.0.1", "-e", "SELECT 1" ] 138 | failureThreshold: 30 139 | periodSeconds: 10 140 | -------------------------------------------------------------------------------- /test/e2e/testscript/schema-lint-destructive.txtar: -------------------------------------------------------------------------------- 1 | env DB_URL=mysql://root:pass@mysql.${NAMESPACE}:3306/myapp 2 | kubectl apply -f database.yaml 3 | kubectl create secret generic db-creds --from-literal=url=${DB_URL} 4 | 5 | # Wait for the first pod created 6 | kubectl-wait-available deploy/mysql 7 | # Wait for the DB ready before creating the schema 8 | kubectl-wait-ready -l app=mysql pods 9 | 10 | # Create the schema 11 | kubectl apply -f schema.yaml 12 | kubectl-wait-ready AtlasSchema/mysql 13 | 14 | # Inspect the schema to ensure it's correct 15 | atlas schema inspect -u ${DB_URL} 16 | cmp stdout schema.hcl 17 | 18 | kubectl patch -f schema.yaml --type merge --patch-file patch-remove-bio.yaml 19 | 20 | # Wait for the controller to detect the change 21 | exec sleep 10 22 | 23 | # Ensure the controller is aware of the change 24 | kubectl wait --for=jsonpath='{.status.conditions[*].reason}'=LintPolicyError --timeout=500s AtlasSchemas/mysql 25 | # Check the error message 26 | kubectl get AtlasSchema/mysql -o jsonpath --template='{.status.conditions[*].message}' 27 | stdout 'destructive changes detected:' 28 | stdout '- Dropping non-virtual column "short_bio"' 29 | -- schema.hcl -- 30 | table "users" { 31 | schema = schema.myapp 32 | column "id" { 33 | null = false 34 | type = int 35 | auto_increment = true 36 | } 37 | column "name" { 38 | null = false 39 | type = varchar(255) 40 | } 41 | column "email" { 42 | null = false 43 | type = varchar(255) 44 | } 45 | column "short_bio" { 46 | null = false 47 | type = varchar(255) 48 | } 49 | primary_key { 50 | columns = [column.id] 51 | } 52 | index "email" { 53 | unique = true 54 | columns = [column.email] 55 | } 56 | } 57 | schema "myapp" { 58 | charset = "utf8mb4" 59 | collate = "utf8mb4_0900_ai_ci" 60 | } 61 | -- patch-remove-bio.yaml -- 62 | spec: 63 | schema: 64 | sql: | 65 | create table users ( 66 | id int not null auto_increment, 67 | name varchar(255) not null, 68 | email varchar(255) unique not null, 69 | primary key (id) 70 | ); 71 | -- schema.yaml -- 72 | apiVersion: db.atlasgo.io/v1alpha1 73 | kind: AtlasSchema 74 | metadata: 75 | name: mysql 76 | spec: 77 | urlFrom: 78 | secretKeyRef: 79 | name: db-creds 80 | key: url 81 | policy: 82 | lint: 83 | destructive: 84 | error: true 85 | schema: 86 | sql: | 87 | create table users ( 88 | id int not null auto_increment, 89 | name varchar(255) not null, 90 | email varchar(255) unique not null, 91 | short_bio varchar(255) not null, 92 | primary key (id) 93 | ); 94 | -- database.yaml -- 95 | apiVersion: v1 96 | kind: Service 97 | metadata: 98 | name: mysql 99 | spec: 100 | selector: 101 | app: mysql 102 | ports: 103 | - name: mysql 104 | port: 3306 105 | targetPort: mysql 106 | type: ClusterIP 107 | --- 108 | apiVersion: apps/v1 109 | kind: Deployment 110 | metadata: 111 | name: mysql 112 | spec: 113 | selector: 114 | matchLabels: 115 | app: mysql 116 | replicas: 1 117 | template: 118 | metadata: 119 | labels: 120 | app: mysql 121 | spec: 122 | containers: 123 | - name: mysql 124 | image: mysql:latest 125 | env: 126 | - name: MYSQL_ROOT_PASSWORD 127 | value: pass 128 | - name: MYSQL_DATABASE 129 | value: myapp 130 | ports: 131 | - containerPort: 3306 132 | name: mysql 133 | startupProbe: 134 | exec: 135 | command: [ "mysql", "-ppass", "-h", "127.0.0.1", "-e", "SELECT 1" ] 136 | failureThreshold: 30 137 | periodSeconds: 10 138 | -------------------------------------------------------------------------------- /test/e2e/testscript/schema-manual-changes.txtar: -------------------------------------------------------------------------------- 1 | env DB_URL=postgres://root:pass@postgres.${NAMESPACE}:5432/postgres?sslmode=disable 2 | kubectl apply -f database.yaml 3 | kubectl create secret generic db-creds --from-literal=url=${DB_URL} 4 | 5 | # Wait for the first pod created 6 | kubectl-wait-available deploy/postgres 7 | # Wait for the DB ready before creating the schema 8 | kubectl-wait-ready -l app=postgres pods 9 | 10 | # Create the secret to store ATLAS_TOKEN 11 | kubectl create secret generic atlas-token --from-literal=ATLAS_TOKEN=${ATLAS_TOKEN} 12 | 13 | # Sync the $WORK directory to the controller pod 14 | kubectl cp -n ${CONTROLLER_NS} ${WORK} ${CONTROLLER}:/tmp/${NAMESPACE}/ 15 | env DEV_URL=postgres://root:pass@postgres.${NAMESPACE}:5433/postgres?sslmode=disable 16 | # List all schema plans and remove them, it may come from previous failure runs 17 | atlas schema plan list --format="{{range .}}{{println .URL}}{{end}}" --dev-url=${DEV_URL} --repo=atlas://atlas-operator --from=file:///tmp/${NAMESPACE}/manual.sql --to=file:///tmp/${NAMESPACE}/schema-v1.hcl 18 | plans-rm stdout 19 | 20 | # Create the schema 21 | kubectl apply -f schema.yaml 22 | kubectl-wait-ready AtlasSchema/postgres 23 | 24 | # Inspect the schema to ensure it's correct 25 | atlas schema inspect -u ${DB_URL} 26 | cmp stdout schema-v1.hcl 27 | 28 | # Make a manual change to the database 29 | atlas schema apply --auto-approve --url=${DB_URL} --dev-url=${DEV_URL} --to=file:///tmp/${NAMESPACE}/manual.sql 30 | 31 | kubectl patch -f schema.yaml --type merge --patch-file patch-comment.yaml 32 | # Ensure the controller is aware of the change 33 | kubectl wait --for=jsonpath='{.status.conditions[*].reason}'=ApprovalPending --timeout=500s AtlasSchemas/postgres 34 | 35 | # Get the plan URL from resource then approve it 36 | kubectl get AtlasSchemas/postgres -o go-template --template='{{ .status.planURL }}' 37 | envfile PLAN_URL=stdout 38 | atlas schema plan approve --url=${PLAN_URL} 39 | 40 | # The schema should be updated now 41 | kubectl-wait-ready AtlasSchemas/postgres 42 | atlas schema inspect -u ${DB_URL} 43 | cmp stdout schema-v1.hcl 44 | 45 | # Cleanup schema plan for the next run 46 | atlas schema plan rm --url=${PLAN_URL} 47 | -- schema-v1.hcl -- 48 | table "t1" { 49 | schema = schema.public 50 | column "id" { 51 | null = false 52 | type = integer 53 | } 54 | column "c1" { 55 | null = true 56 | type = integer 57 | } 58 | primary_key { 59 | columns = [column.id] 60 | } 61 | } 62 | schema "public" { 63 | comment = "standard public schema" 64 | } 65 | -- patch-comment.yaml -- 66 | spec: 67 | schema: 68 | sql: | 69 | -- comment 70 | create table t1 ( 71 | id int not null, 72 | c1 int, 73 | primary key (id) 74 | ); 75 | -- manual.sql -- 76 | create table t1 ( 77 | id int not null, 78 | c1 int, 79 | c2 int, 80 | primary key (id) 81 | ); 82 | -- schema.yaml -- 83 | apiVersion: db.atlasgo.io/v1alpha1 84 | kind: AtlasSchema 85 | metadata: 86 | name: postgres 87 | spec: 88 | urlFrom: 89 | secretKeyRef: 90 | name: db-creds 91 | key: url 92 | cloud: 93 | repo: atlas-operator 94 | tokenFrom: 95 | secretKeyRef: 96 | name: atlas-token 97 | key: ATLAS_TOKEN 98 | schema: 99 | sql: | 100 | create table t1 ( 101 | id int not null, 102 | c1 int, 103 | primary key (id) 104 | ); 105 | -- database.yaml -- 106 | apiVersion: v1 107 | kind: Service 108 | metadata: 109 | name: postgres 110 | spec: 111 | selector: 112 | app: postgres 113 | ports: 114 | - name: postgres 115 | port: 5432 116 | targetPort: postgres 117 | - name: postgres-dev 118 | port: 5433 119 | targetPort: postgres-dev 120 | type: ClusterIP 121 | --- 122 | apiVersion: apps/v1 123 | kind: Deployment 124 | metadata: 125 | name: postgres 126 | spec: 127 | selector: 128 | matchLabels: 129 | app: postgres 130 | replicas: 1 131 | template: 132 | metadata: 133 | labels: 134 | app: postgres 135 | spec: 136 | securityContext: 137 | runAsNonRoot: true 138 | runAsUser: 999 139 | containers: 140 | - name: postgres 141 | image: postgres:15.4 142 | securityContext: 143 | allowPrivilegeEscalation: false 144 | capabilities: 145 | drop: 146 | - all 147 | env: 148 | - name: POSTGRES_PASSWORD 149 | value: pass 150 | - name: POSTGRES_USER 151 | value: root 152 | ports: 153 | - containerPort: 5432 154 | name: postgres 155 | startupProbe: 156 | exec: 157 | command: [ "pg_isready" ] 158 | failureThreshold: 30 159 | periodSeconds: 10 160 | - name: postgres-dev 161 | image: postgres:15.4 162 | securityContext: 163 | allowPrivilegeEscalation: false 164 | capabilities: 165 | drop: 166 | - all 167 | env: 168 | - name: POSTGRES_PASSWORD 169 | value: pass 170 | - name: POSTGRES_USER 171 | value: root 172 | - name: PGPORT 173 | value: "5433" 174 | ports: 175 | - containerPort: 5433 176 | name: postgres-dev 177 | startupProbe: 178 | exec: 179 | command: [ "pg_isready" ] 180 | failureThreshold: 30 181 | periodSeconds: 10 182 | -------------------------------------------------------------------------------- /test/e2e/testscript/schema-multi-tenancy.txtar: -------------------------------------------------------------------------------- 1 | env DB_URL=postgres://root:pass@postgres.${NAMESPACE}:5432/postgres?sslmode=disable 2 | env DB_DEV_URL=postgres://root:pass@postgres.${NAMESPACE}:5433/postgres?sslmode=disable 3 | kubectl apply -f database.yaml 4 | kubectl create secret generic db-creds --from-literal=url=${DB_URL} 5 | kubectl create configmap db-dev-creds --from-literal=url=${DB_DEV_URL} 6 | kubectl create secret generic atlas-token --from-literal=ATLAS_TOKEN=${ATLAS_TOKEN} 7 | 8 | # Wait for the first pod created 9 | kubectl-wait-available deploy/postgres 10 | # Wait for the DB ready before creating the schema 11 | kubectl-wait-ready -l app=postgres pods 12 | 13 | # Create the schema 14 | kubectl exec -i deploy/postgres -- psql -U root -d postgres -c 'CREATE SCHEMA tenant_1;' 15 | kubectl exec -i deploy/postgres -- psql -U root -d postgres -c 'CREATE SCHEMA tenant_2;' 16 | 17 | # Inspect the schema to ensure it's correct 18 | atlas schema inspect -u ${DB_URL} 19 | cmp stdout schema.hcl 20 | 21 | kubectl apply -f multi-tenancy.yaml 22 | kubectl wait --for=jsonpath='{.status.conditions[*].reason}'=ReadSchema --timeout=500s AtlasSchemas/multi-tenancy 23 | 24 | -- multi-tenancy.yaml -- 25 | apiVersion: db.atlasgo.io/v1alpha1 26 | kind: AtlasSchema 27 | metadata: 28 | name: multi-tenancy 29 | spec: 30 | envName: "test" 31 | schema: 32 | sql: | 33 | create table users ( 34 | id int not null, 35 | name varchar(255) not null, 36 | email varchar(255) unique not null, 37 | short_bio varchar(255) not null, 38 | primary key (id) 39 | ); 40 | cloud: 41 | repo: atlas-operator 42 | tokenFrom: 43 | secretKeyRef: 44 | name: atlas-token 45 | key: ATLAS_TOKEN 46 | vars: 47 | - key: "db_url" 48 | valueFrom: 49 | secretKeyRef: 50 | name: db-creds 51 | key: url 52 | - key: "dev_db_url" 53 | valueFrom: 54 | configMapKeyRef: 55 | name: db-dev-creds 56 | key: url 57 | config: | 58 | variable "db_url" { 59 | type = string 60 | } 61 | variable "dev_db_url" { 62 | type = string 63 | } 64 | data "sql" "tenants" { 65 | url = var.db_url 66 | query = <