diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7a8995bd..03dcbe94 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -14,26 +14,27 @@ on: workflow_dispatch: inputs: deploy: - description: "Deploy location" + description: "Which environment to deploy to" required: true default: "none" type: choice options: - - production - - staging + - prod + - test - none concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + DEPLOY: ${{ (inputs.deploy != 'none' && inputs.deploy) || ((github.event_name == 'workflow_dispatch' && github.event.inputs.deploy == 'prod') || (github.event_name == 'push' && github.ref_type == 'branch' && github.ref_name == 'master') && 'prod') || ((github.event_name == 'workflow_dispatch' && github.event.inputs.deploy == 'test') || (github.event_name == 'push' && github.ref_type == 'branch' && github.ref_name == 'dev') || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'staged')) && 'test') || 'none' }} + jobs: ci: name: API Lint, Build, Test, Deploy runs-on: aws-runner env: - DEPLOY_PROD: ${{ (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy == 'production') || (github.event_name == 'push' && github.ref_type == 'branch' && github.ref_name == 'master') }} - DEPLOY_STAGE: ${{ (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy == 'staging') || (github.event_name == 'push' && github.ref_type == 'branch' && github.ref_name == 'dev') || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'staged')) }} GOLANGCI_LINT_CACHE: /home/runner/.cache/golangci-lint concurrency: group: ${{ github.workflow }}-ci-${{ github.ref }} @@ -99,7 +100,7 @@ jobs: run: make generate - name: Build Dev Portal Frontend - run: make ${{ (env.DEPLOY_PROD == 'true' && 'portal') || 'portal_stage' }} + run: make ${{ (env.DEPLOY == 'prod' && 'portal') || 'portal_stage' }} - name: Run Linter run: make lint @@ -110,64 +111,155 @@ jobs: - name: Build App run: make build - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 - if: ${{ env.DEPLOY_PROD == 'true' || env.DEPLOY_STAGE == 'true' }} - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: ${{ secrets.AWS_REGION }} - - - name: Login to Amazon ECR - id: login-ecr - if: ${{ env.DEPLOY_PROD == 'true' || env.DEPLOY_STAGE == 'true' }} - uses: aws-actions/amazon-ecr-login@v1 - - name: Make build context - if: ${{ env.DEPLOY_PROD == 'true' || env.DEPLOY_STAGE == 'true' }} + if: env.DEPLOY != 'none' run: | docker context create builders - name: Setup buildx uses: docker/setup-buildx-action@v2 - if: ${{ env.DEPLOY_PROD == 'true' || env.DEPLOY_STAGE == 'true' }} + if: env.DEPLOY != 'none' with: install: true endpoint: builders + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + - name: Build docker image uses: docker/build-push-action@v4 - if: ${{ env.DEPLOY_PROD == 'true' || env.DEPLOY_STAGE == 'true' }} + if: env.DEPLOY != 'none' with: context: . file: docker/partial.Dockerfile - # cache-from: | - # type=registry,ref=gha - # cache-to: | - # type=registry,ref=gha,mode=max tags: | - ${{ steps.login-ecr.outputs.registry }}/${{ (env.DEPLOY_PROD == 'true' && '7tv') || '7tv-stage' }}/api:latest - ${{ steps.login-ecr.outputs.registry }}/${{ (env.DEPLOY_PROD == 'true' && '7tv') || '7tv-stage' }}/api:${{ github.sha }} + ghcr.io/seventv/api:${{ env.DEPLOY }}-${{ github.sha }} + ghcr.io/seventv/api:${{ env.DEPLOY }}-latest push: true - - name: Update deployment template - uses: danielr1996/envsubst-action@1.1.0 - if: ${{ env.DEPLOY_PROD == 'true' || env.DEPLOY_STAGE == 'true' }} - env: - IMAGE: ${{ steps.login-ecr.outputs.registry }}/${{ (env.DEPLOY_PROD == 'true' && '7tv') || '7tv-stage' }}/api:${{ github.sha }} - with: - input: k8s/${{ (env.DEPLOY_PROD == 'true' && 'production') || 'staging' }}.template.yaml - output: k8s/deploy.yaml + validate: + name: API Deploy Validation + needs: ci + runs-on: ubuntu-latest + permissions: + pull-requests: write + defaults: + run: + working-directory: ./terraform - - name: Setup Kubectl - uses: azure/setup-kubectl@v3.0 + steps: + - name: Checkout code + id: ok + if: env.DEPLOY != 'none' + uses: actions/checkout@v3 + + - name: "Setup Terraform" + if: steps.ok.outcome == 'success' + uses: hashicorp/setup-terraform@v1 + with: + cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }} - - name: Deploy to k8s - if: ${{ env.DEPLOY_PROD == 'true' || env.DEPLOY_STAGE == 'true' }} + - name: "Terraform Init" + if: steps.ok.outcome == 'success' + id: init env: - KUBE_CONFIG_DATA: ${{ (env.DEPLOY_PROD == 'true' && secrets.KUBECONFIG) || secrets.KUBECONFIG_STAGE }} + TF_WORKSPACE: ${{ env.DEPLOY }} + run: terraform init + continue-on-error: true + + - name: "Terraform Workspace" + if: steps.ok.outcome == 'success' + run: terraform workspace select -or-create=true ${{ env.DEPLOY }} + + - name: Terraform fmt + if: steps.ok.outcome == 'success' + id: fmt + run: terraform fmt -check + continue-on-error: true + + - name: Terraform Validate + if: steps.ok.outcome == 'success' + id: validate + run: terraform validate -no-color + + - name: Terraform Variables + if: steps.ok.outcome == 'success' run: | - mkdir -p ~/.kube - (echo $KUBE_CONFIG_DATA | base64 -d) >> ~/.kube/config + cat < *.auto.tfvars + image_url="ghcr.io/seventv/api:${{ env.DEPLOY }}-${{ github.sha }}" + image_pull_policy="IfNotPresent" + + EOF + + - name: "Terraform Plan" + if: steps.ok.outcome == 'success' + id: plan + run: terraform plan -no-color + + - uses: actions/github-script@v6 + if: steps.ok.outcome == 'success' && github.event_name == 'pull_request' + env: + PLAN: "terraform\n${{ steps.plan.outputs.stdout }}" + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + // 1. Retrieve existing bot comments for the PR + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }) + const botComment = comments.find(comment => { + return comment.user.type === 'Bot' && comment.body.includes('Terraform Format and Style') + }) + + // 2. Prepare format of the comment + const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` + #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` + #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` +
Validation Output + + \`\`\`\n + ${{ steps.validate.outputs.stdout }} + \`\`\` + +
+ + #### Terraform Plan 📖\`${{ steps.plan.outcome }}\` + +
Show Plan + + \`\`\`\n + ${process.env.PLAN} + \`\`\` + +
+ + *Actor: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Workflow: \`${{ github.workflow }}\`*`; + + // 3. If we have a comment, update it, otherwise create a new one + if (botComment) { + github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: output + }) + } else { + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: output + }) + } - kubectl apply -f k8s/deploy.yaml + - name: "Terraform Apply" + if: steps.ok.outcome == 'success' + id: apply + run: terraform apply -no-color -auto-approve + continue-on-error: true diff --git a/.gitignore b/.gitignore index a572aa1f..139dc6cc 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ out/ tmp/ *.yaml !example.config.yaml +!terraform/config.template.yaml !docker-compose.yaml !.github/**/*.yaml *-gqlgen.go @@ -16,3 +17,17 @@ internal/rest/v*/docs go.work go.work.sum !.golangci.yaml + +# Terraform local state files +**/.terraform/* +*.tfstate +*.tfstate.* +*.tfplan +crash.log +*.tfvars +override.tf +override.tf.json +*_override.tf +*_override.tf.json +.terraformrc +terraform.rc diff --git a/Makefile b/Makefile index c9499b98..d1c1ebad 100644 --- a/Makefile +++ b/Makefile @@ -70,3 +70,9 @@ work: dev: go run cmd/main.go + +terraform: + terraform -chdir=./terraform init + +deploy: + terraform -chdir=./terraform apply -auto-approve diff --git a/cmd/main.go b/cmd/main.go index 11b8cccd..df389644 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -105,9 +105,11 @@ func main() { { gctx.Inst().Mongo, err = mongo.Setup(gctx, mongo.SetupOptions{ - URI: config.Mongo.URI, - DB: config.Mongo.DB, - Direct: config.Mongo.Direct, + URI: config.Mongo.URI, + DB: config.Mongo.DB, + Direct: config.Mongo.Direct, + Username: config.Mongo.Username, + Password: config.Mongo.Password, }) if err != nil { zap.S().Fatalw("failed to setup mongo handler", diff --git a/data/events/payload.go b/data/events/payload.go index 685cf15f..1ad52da7 100644 --- a/data/events/payload.go +++ b/data/events/payload.go @@ -19,10 +19,16 @@ type AnyPayload interface { } type HelloPayload struct { - HeartbeatInterval uint32 `json:"heartbeat_interval"` - SessionID string `json:"session_id"` - SubscriptionLimit int32 `json:"subscription_limit"` - Actor *primitive.ObjectID `json:"actor,omitempty"` + HeartbeatInterval uint32 `json:"heartbeat_interval"` + SessionID string `json:"session_id"` + SubscriptionLimit int32 `json:"subscription_limit"` + Actor *primitive.ObjectID `json:"actor,omitempty"` + Instance HelloPayloadInstanceInfo `json:"instance,omitempty"` +} + +type HelloPayloadInstanceInfo struct { + Name string `json:"name"` + Population int32 `json:"population"` } type AckPayload struct { diff --git a/go.mod b/go.mod index 9ff31502..fc36cc1c 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/prometheus/client_golang v1.14.0 - github.com/seventv/common v0.0.0-20230528214454-1a842fd909aa + github.com/seventv/common v0.0.0-20230729093701-30b85f9e2678 github.com/seventv/compactdisc v0.0.0-20221006190906-ccfe99954e48 github.com/seventv/image-processor/go v0.0.0-20221128171540-d050701ac324 github.com/seventv/message-queue/go v0.0.0-20220721124044-9fd23bda9643 diff --git a/go.sum b/go.sum index 29b90028..a1ac71ae 100644 --- a/go.sum +++ b/go.sum @@ -49,9 +49,13 @@ github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= @@ -71,6 +75,7 @@ github.com/aws/aws-sdk-go-v2/service/sqs v1.20.2/go.mod h1:1ttxGjUHZliCQMpPss1sU github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bsm/ginkgo/v2 v2.5.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= @@ -113,9 +118,11 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= @@ -246,10 +253,12 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM= @@ -289,6 +298,7 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.7.0 h1:r3y12KyNxj/Sb/iOE46ws+3mS1+MZca1wlHQFPsY/JU= github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nicklaw5/helix v1.25.0 h1:Mrz537izZVsGdM3I46uGAAlslj61frgkhS/9xQqyT/M= github.com/nicklaw5/helix v1.25.0/go.mod h1:yvXZFapT6afIoxnAvlWiJiUMsYnoHl7tNs+t0bloAMw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -300,6 +310,7 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -338,12 +349,15 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/seventv/common v0.0.0-20230528214454-1a842fd909aa h1:kIYvE8Ii1VmbTNQzI22QMiufnQZeG3vWURrCHd4ZH2I= github.com/seventv/common v0.0.0-20230528214454-1a842fd909aa/go.mod h1:jHHFe3uNMyzb/ReDqMvaI/A1euvV/PW/G+2PhcWQqWw= +github.com/seventv/common v0.0.0-20230729093701-30b85f9e2678 h1:zhFPVeJYPyOldp/DM2Sntjky04mFsp6O5WopISLcLVc= +github.com/seventv/common v0.0.0-20230729093701-30b85f9e2678/go.mod h1:jHHFe3uNMyzb/ReDqMvaI/A1euvV/PW/G+2PhcWQqWw= github.com/seventv/compactdisc v0.0.0-20221006190906-ccfe99954e48 h1:IqWrtt/yob45YnOQ5Wwkbf8qP22eKVtg0WzfyEkGnFg= github.com/seventv/compactdisc v0.0.0-20221006190906-ccfe99954e48/go.mod h1:T+ldp0YQe03s44+A5HHHI/jB3ZmWqOIaNouEqAS+1Dk= github.com/seventv/image-processor/go v0.0.0-20221128171540-d050701ac324 h1:iU3wWepRTbkNoTAPR23m6TAW6Yb9pOMCYVr0K++OBAw= github.com/seventv/image-processor/go v0.0.0-20221128171540-d050701ac324/go.mod h1:0AAXPHKWVVbOdHlfSsL4xwRAxSYgI+v/XPHJeb8XbLs= github.com/seventv/message-queue/go v0.0.0-20220721124044-9fd23bda9643 h1:7DAXa8jZl3f3mltSEaqLFir+qLkDKx/J04LcxcpdEhA= github.com/seventv/message-queue/go v0.0.0-20220721124044-9fd23bda9643/go.mod h1:L1iYDSmltUnxlVGX9RayCoVi3e8aNtFrrLJL6Jv+mrM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= @@ -771,6 +785,7 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/configure/config.go b/internal/configure/config.go index bfd48b8c..d6dd1586 100644 --- a/internal/configure/config.go +++ b/internal/configure/config.go @@ -141,9 +141,11 @@ type Config struct { } `mapstructure:"redis" json:"redis"` Mongo struct { - URI string `mapstructure:"uri" json:"uri"` - DB string `mapstructure:"db" json:"db"` - Direct bool `mapstructure:"direct" json:"direct"` + URI string `mapstructure:"uri" json:"uri"` + Username string `mapstructure:"username" json:"username"` + Password string `mapstructure:"password" json:"password"` + DB string `mapstructure:"db" json:"db"` + Direct bool `mapstructure:"direct" json:"direct"` } `mapstructure:"mongo" json:"mongo"` Health struct { @@ -277,6 +279,7 @@ type Config struct { } `mapstructure:"message_queue" json:"message_queue"` S3 struct { + Enabled bool `mapstructure:"enabled" json:"enabled"` AccessToken string `mapstructure:"access_token" json:"access_token"` SecretKey string `mapstructure:"secret_key" json:"secret_key"` Region string `mapstructure:"region" json:"region"` diff --git a/internal/svc/health/health.go b/internal/svc/health/health.go index a1e2afb2..abcd5e76 100644 --- a/internal/svc/health/health.go +++ b/internal/svc/health/health.go @@ -9,7 +9,7 @@ import ( "go.uber.org/zap" ) -func New(gCtx global.Context) <-chan struct{} { +func New(gctx global.Context) <-chan struct{} { done := make(chan struct{}) srv := fasthttp.Server{ @@ -29,9 +29,9 @@ func New(gCtx global.Context) <-chan struct{} { mongoDown bool ) - if gCtx.Inst().Redis != nil { + if gctx.Inst().Redis != nil { lCtx, cancel := context.WithTimeout(context.Background(), time.Second*5) - if err := gCtx.Inst().Redis.Ping(lCtx); err != nil { + if err := gctx.Inst().Redis.Ping(lCtx); err != nil { zap.S().Warnw("redis is not responding", "error", err, ) @@ -41,15 +41,15 @@ func New(gCtx global.Context) <-chan struct{} { } lCtx, cancel := context.WithTimeout(context.Background(), time.Second*5) - mqDown = gCtx.Inst().MessageQueue != nil && !gCtx.Inst().MessageQueue.Connected(lCtx) + mqDown = gctx.Inst().MessageQueue != nil && !gctx.Inst().MessageQueue.Connected(lCtx) cancel() if mqDown { zap.S().Warnw("mq is not connected") } - if gCtx.Inst().S3 != nil { + if gctx.Config().S3.Enabled && gctx.Inst().S3 != nil { lCtx, cancel := context.WithTimeout(context.Background(), time.Second*5) - if _, err := gCtx.Inst().S3.ListBuckets(lCtx); err != nil { + if _, err := gctx.Inst().S3.ListBuckets(lCtx); err != nil { s3Down = true zap.S().Warnw("s3 is not responding", "error", err, @@ -58,9 +58,9 @@ func New(gCtx global.Context) <-chan struct{} { cancel() } - if gCtx.Inst().Mongo != nil { + if gctx.Inst().Mongo != nil { lCtx, cancel := context.WithTimeout(context.Background(), time.Second*5) - if err := gCtx.Inst().Mongo.Ping(lCtx); err != nil { + if err := gctx.Inst().Mongo.Ping(lCtx); err != nil { mongoDown = true zap.S().Warnw("mongo is not responding", "error", err, @@ -78,10 +78,10 @@ func New(gCtx global.Context) <-chan struct{} { go func() { defer close(done) zap.S().Infow("Health enabled", - "bind", gCtx.Config().Health.Bind, + "bind", gctx.Config().Health.Bind, ) - if err := srv.ListenAndServe(gCtx.Config().Health.Bind); err != nil { + if err := srv.ListenAndServe(gctx.Config().Health.Bind); err != nil { zap.S().Fatalw("failed to bind health", "error", err, ) @@ -89,7 +89,7 @@ func New(gCtx global.Context) <-chan struct{} { }() go func() { - <-gCtx.Done() + <-gctx.Done() _ = srv.Shutdown() }() diff --git a/internal/svc/health/health_test.go b/internal/svc/health/health_test.go index 377079b5..c2ddd68c 100644 --- a/internal/svc/health/health_test.go +++ b/internal/svc/health/health_test.go @@ -18,17 +18,17 @@ func TestHealth(t *testing.T) { config.Health.Enabled = true config.Health.Bind = "127.0.1.1:3000" - gCtx, cancel := global.WithCancel(global.New(context.Background(), config)) + gctx, cancel := global.WithCancel(global.New(context.Background(), config)) var err error - gCtx.Inst().S3, err = s3.NewMock(gCtx, map[string]map[string][]byte{}) + gctx.Inst().S3, err = s3.NewMock(gctx, map[string]map[string][]byte{}) testutil.IsNil(t, err, "s3 init successful") - gCtx.Inst().MessageQueue, err = messagequeue.New(gCtx, messagequeue.ConfigMock{}) + gctx.Inst().MessageQueue, err = messagequeue.New(gctx, messagequeue.ConfigMock{}) testutil.IsNil(t, err, "mq init successful") - mq, _ := gCtx.Inst().MessageQueue.(*messagequeue.InstanceMock) - s3, _ := gCtx.Inst().S3.(*s3.MockInstance) + mq, _ := gctx.Inst().MessageQueue.(*messagequeue.InstanceMock) + s3, _ := gctx.Inst().S3.(*s3.MockInstance) // TODO we need to mock redis :-) // gCtx.Inst().Redis, err = redis.NewMock() @@ -38,7 +38,7 @@ func TestHealth(t *testing.T) { // gCtx.Inst().Mongo, err = mongo.NewMock() // testutil.IsNil(t, err, "mongo init successful") - done := New(gCtx) + done := New(gctx) time.Sleep(time.Millisecond * 50) @@ -57,13 +57,16 @@ func TestHealth(t *testing.T) { testutil.Assert(t, http.StatusInternalServerError, resp.StatusCode, "response code rmq down") mq.SetConnected(true) - s3.SetConnected(false) - resp, err = http.DefaultClient.Get("http://127.0.1.1:3000") - testutil.IsNil(t, err, "No error") + if config.S3.Enabled { + s3.SetConnected(gctx.Config().S3.Enabled) - _ = resp.Body.Close() - testutil.Assert(t, http.StatusInternalServerError, resp.StatusCode, "response code s3 down") + resp, err = http.DefaultClient.Get("http://127.0.1.1:3000") + testutil.IsNil(t, err, "No error") + + _ = resp.Body.Close() + testutil.Assert(t, http.StatusInternalServerError, resp.StatusCode, "response code s3 down") + } cancel() diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl new file mode 100644 index 00000000..8c8b71c2 --- /dev/null +++ b/terraform/.terraform.lock.hcl @@ -0,0 +1,45 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.7.0" + constraints = "~> 5.7.0" + hashes = [ + "h1:A7p0npQ+UHlnapVuikOzhmgchAq8agtfZGkYiiEOnp0=", + "zh:03240d7fc041d5331db7fd5f2ca4fe031321d07d2a6ca27085c5020dae13f211", + "zh:0b5252b14c354636fe0348823195dd901b457de1a033015f4a7d11cfe998c766", + "zh:2bfb62325b0487be8d1850a964f09cca0d45148faec577459c2a24334ec9977b", + "zh:2f9e317ffc57d2b5117cfe8dc266f88aa139b760bc93d8adeed7ad533a78b5a3", + "zh:36512725c9d7c559927b98fead04be58494a3a997e5270b905a75a468e307427", + "zh:5483e696d3ea764f746d3fe439f7dcc49001c3c774122d7baa51ce01011f0075", + "zh:5967635cc14f969ea26622863a2e3f9d6a7ddd3e7d35a29a7275c5e10579ac8c", + "zh:7e63c94a64af5b7aeb36ea6e3719962f65a7c28074532c02549a67212d410bb8", + "zh:8a7d5f33b11a3f5c7281413b431fa85de149ed8493ec1eea73d50d2d80a475e6", + "zh:8e2ed2d986aaf590975a79a2f6b5e60e0dc7d804ab01a8c03ab181e41cfe9b0f", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9c7b8ca1b17489f16a6d0f1fc2aa9c130978ea74c9c861d8435410567a0a888f", + "zh:a54385896a70524063f0c5420be26ff6f88909bd8e6902dd3e922577b21fd546", + "zh:aecd3a8fb70b938b58d93459bfb311540fd6aaf981924bf34abd48f953b4be0d", + "zh:f3de076fa3402768d27af0187c6a677777b47691d1f0f84c9b259ff66e65953e", + ] +} + +provider "registry.terraform.io/hashicorp/kubernetes" { + version = "2.18.1" + constraints = "2.18.1" + hashes = [ + "h1:h4ezMuMNyKRMRhlrErph7QOUToc77U+rVKdR48w6tr8=", + "zh:09d69d244f5e688d9b1582112aa5d151c5336278e43d39c88ae920c26536b753", + "zh:0df4c988056f7d84d9161c6c955ad7346364c261d100ef510a6cc7fa4a235197", + "zh:2d3d0cb2931b6153a7971ce8c6fae92722b1116e16f42abbaef115dba895c8d8", + "zh:47830e8fc1760860bfa4aaf418627ff3c6ffcac6cebbbc490e5e0e6b31287d80", + "zh:49467177b514bada0fb3b6982897a347498af8ef9ef8d9fd611fe21dfded2e25", + "zh:5c7eae2c51ba175822730a63ad59cf41604c76c46c5c97332506ab42023525ce", + "zh:6efae755f02df8ab65ce7a831f33bd4817359db205652fd4bc4b969302072b15", + "zh:7e6e97b79fecd25aaf0f4fb91da945a65c36fe2ba2a4313288a60ede55506aad", + "zh:b75f2c9dd24b355ffe73e7b2fcd3145fc32735068f0ec2eba2df63f792dd16e8", + "zh:dbef9698d842eb49a846db6d7694f159ae5154ffbb7a753a9d4cab88c462a6d4", + "zh:f1b1fd580d92eedd9c8224d463997ccff1a62851fea65106aac299efe9ab622a", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} diff --git a/terraform/config.template.yaml b/terraform/config.template.yaml new file mode 100644 index 00000000..dab224b1 --- /dev/null +++ b/terraform/config.template.yaml @@ -0,0 +1,91 @@ +level: info + +website_url: ${website_url} +cdn_url: ${cdn_url} + +http: + addr: ${http_addr} + ports: + gql: ${http_port_gql} + rest: ${http_port_rest} + + cookie: + domain: ${cookie_domain} + secure: ${cookie_secure} + whitelist: + - ${website_url} + - https://twitch.tv + - https://www.twitch.tv + - https://kick.com + +limits: + max_page: 25 + buckets: + gql_v3: [250, 2] + image_processing: [20, 60] + emotes: + max_tags: 6 + max_width: 1000 + max_height: 1000 + quota: + max_active_mod_requests: 3 + +platforms: + twitch: + client_id: ${twitch_client_id} + client_secret: ${twitch_client_secret} + redirect_uri: ${twitch_redirect_uri} + discord: + client_id: ${discord_client_id} + client_secret: ${discord_client_secret} + redirect_uri: ${discord_redirect_uri} + api: ${discord_api} + channels: ${discord_channels} + kick: + challenge_token: ${kick_challenge_token} + +monitoring: + enabled: true + bind: 0.0.0.0:9100 + +pprof: + enabled: false + bind: 0.0.0.0:9300 + +mongo: + uri: ${mongo_uri} + db: ${mongo_database} + username: ${mongo_username} + password: ${mongo_password} + +redis: + addresses: + - ${redis_address} + sentinel: true + master_name: "mymaster" + username: ${redis_username} + password: ${redis_password} + +message_queue: + mode: "RMQ" + image_processor_jobs_queue_name: "seventv_image_processor_jobs" + image_processor_results_queue_name: "seventv_image_processor_results" + image_processor_user_pictures_results_queue_name: "seventv_image_processor_user_pictures_results" + rmq: + uri: ${rmq_uri} + max_reconnect_attempts: 25 + +s3: + region: "${s3_region}" + access_token: "${s3_access_key}" + secret_key: "${s3_secret_key}" + internal_bucket: "${s3_internal_bucket}" + public_bucket: "${s3_public_bucket}" + endpoint: "${s3_endpoint}" + +health: + enabled: true + bind: 0.0.0.0:9200 + +credentials: + jwt_secret: ${jwt_secret} diff --git a/terraform/deployment.tf b/terraform/deployment.tf new file mode 100644 index 00000000..0b340ce0 --- /dev/null +++ b/terraform/deployment.tf @@ -0,0 +1,412 @@ +data "kubernetes_namespace" "app" { + metadata { + name = var.namespace + } +} + +resource "kubernetes_secret" "app" { + metadata { + name = "api" + namespace = var.namespace + } + + data = { + "config.yaml" = templatefile("${path.module}/config.template.yaml", { + bind = "0.0.0.0:3000" + website_url = "https://${local.infra.primary_zone}" + cdn_url = var.cdn_url + http_addr = var.http_addr + http_port_gql = var.http_port_gql + http_port_rest = var.http_port_rest + cookie_domain = local.infra.secondary_zone + cookie_secure = true + twitch_client_id = var.twitch_client_id + twitch_client_secret = var.twitch_client_secret + twitch_redirect_uri = "https://${local.infra.secondary_zone}/v3/auth?platform=twitch&callback=true" + discord_client_id = var.discord_client_id + discord_client_secret = var.discord_client_secret + discord_redirect_uri = var.discord_redirect_uri + discord_api = "" + discord_channels = yamlencode([]) + kick_challenge_token = "" + mongo_uri = local.infra.mongodb_uri + mongo_username = local.infra.mongodb_user_app.username + mongo_password = local.infra.mongodb_user_app.password + mongo_database = "7tv" + redis_address = local.infra.redis_host + redis_username = "default" + redis_password = local.infra.redis_password + rmq_uri = local.infra.rabbitmq_uri + s3_region = local.s3.region + s3_access_key = local.s3.ak + s3_secret_key = local.s3.sk + s3_internal_bucket = local.s3.internal_bucket + s3_public_bucket = local.s3.public_bucket + s3_endpoint = local.s3.endpoint != null ? local.s3.endpoint : "" + jwt_secret = random_id.jwt-secret.hex + }) + } +} + +resource "random_id" "jwt-secret" { + byte_length = 64 +} + +resource "kubernetes_deployment" "app" { + metadata { + name = "api" + namespace = data.kubernetes_namespace.app.metadata[0].name + labels = { + app = "api" + } + } + + lifecycle { + replace_triggered_by = [kubernetes_secret.app] + } + + timeouts { + create = "4m" + update = "2m" + delete = "2m" + } + + spec { + selector { + match_labels = { + app = "api" + } + } + + strategy { + type = "RollingUpdate" + rolling_update { + max_surge = "0" + max_unavailable = "1" + } + } + + template { + metadata { + labels = { + app = "api" + } + } + + spec { + container { + name = "api" + image = local.image_url + + port { + name = "gql" + container_port = 3000 + protocol = "TCP" + } + + port { + name = "rest" + container_port = 3100 + protocol = "TCP" + } + + port { + name = "portal" + container_port = 3200 + protocol = "TCP" + } + + port { + name = "metrics" + container_port = 9100 + protocol = "TCP" + } + + port { + name = "health" + container_port = 9200 + protocol = "TCP" + } + + port { + name = "pprof" + container_port = 9300 + protocol = "TCP" + } + + port { + name = "eventbridge" + container_port = 9700 + protocol = "TCP" + } + + env { + name = "API_K8S_POD_NAME" + value_from { + field_ref { + field_path = "metadata.name" + } + } + } + + resources { + requests = { + cpu = local.infra.production ? "1500m" : "100m" + memory = local.infra.production ? "4Gi" : "600Mi" + } + limits = { + cpu = local.infra.production ? "1750m" : "150m" + memory = local.infra.production ? "4.25Gi" : "700Mi" + } + } + + volume_mount { + name = "config" + mount_path = "/app/config.yaml" + sub_path = "config.yaml" + } + + liveness_probe { + http_get { + port = "health" + path = "/" + } + initial_delay_seconds = 10 + timeout_seconds = 5 + period_seconds = 5 + success_threshold = 1 + failure_threshold = 4 + } + + readiness_probe { + http_get { + port = "health" + path = "/" + } + initial_delay_seconds = 10 + timeout_seconds = 5 + period_seconds = 5 + success_threshold = 1 + failure_threshold = 3 + } + + image_pull_policy = var.image_pull_policy + } + + volume { + name = "config" + secret { + secret_name = kubernetes_secret.app.metadata[0].name + } + } + } + } + } +} + +resource "kubernetes_service" "app" { + metadata { + name = "api" + namespace = data.kubernetes_namespace.app.metadata[0].name + } + + spec { + selector = { + app = "api" + } + + port { + name = "gql" + port = 3000 + target_port = "gql" + } + + port { + name = "rest" + port = 3100 + target_port = "rest" + } + + port { + name = "portal" + port = 3200 + target_port = "portal" + } + + port { + name = "metrics" + port = 9100 + target_port = "metrics" + } + + port { + name = "health" + port = 9200 + target_port = "health" + } + + port { + name = "pprof" + port = 9300 + target_port = "pprof" + } + + port { + name = "eventbridge" + port = 9700 + target_port = "eventbridge" + } + } +} + +resource "kubernetes_ingress_v1" "app" { + metadata { + name = "api" + namespace = data.kubernetes_namespace.app.metadata[0].name + annotations = { + "kubernetes.io/ingress.class" = "nginx" + "external-dns.alpha.kubernetes.io/cloudflare-proxied" = "true" + "nginx.ingress.kubernetes.io/limit-connections" : "64" + "nginx.ingress.kubernetes.io/proxy-body-size" : "7m" + } + } + + spec { + rule { + host = local.infra.secondary_zone + http { + // Developer Portal + path { + path = "/" + path_type = "Prefix" + backend { + service { + name = kubernetes_service.app.metadata[0].name + port { + name = "portal" + } + } + } + } + + // GraphQL API - V3 + path { + path = "/v3/gql" + path_type = "Prefix" + backend { + service { + name = kubernetes_service.app.metadata[0].name + port { + name = "gql" + } + } + } + } + + // REST API - V3 + path { + path = "/v3" + path_type = "Prefix" + backend { + service { + name = kubernetes_service.app.metadata[0].name + port { + name = "rest" + } + } + } + } + + // GraphQL API - V2 + path { + path = "/v2/gql" + path_type = "Prefix" + backend { + service { + name = kubernetes_service.app.metadata[0].name + port { + name = "gql" + } + } + } + } + + // REST API - V2 + path { + path = "/v2" + path_type = "Prefix" + backend { + service { + name = kubernetes_service.app.metadata[0].name + port { + name = "rest" + } + } + } + } + } + } + } +} + +resource "kubernetes_horizontal_pod_autoscaler_v2" "api" { + metadata { + name = "api" + namespace = data.kubernetes_namespace.app.metadata[0].name + } + + spec { + scale_target_ref { + api_version = "apps/v1" + kind = "Deployment" + name = kubernetes_deployment.app.metadata[0].name + } + + min_replicas = 2 + max_replicas = 8 + + metric { + type = "Resource" + resource { + name = "memory" + target { + type = "Utilization" + average_utilization = 60 + } + } + } + + metric { + type = "Resource" + resource { + name = "cpu" + target { + type = "Utilization" + average_utilization = 80 + } + } + } + + behavior { + scale_down { + stabilization_window_seconds = 300 + select_policy = "Min" + policy { + type = "Pods" + value = 1 + period_seconds = 15 + } + } + + scale_up { + stabilization_window_seconds = 120 + select_policy = "Min" + policy { + type = "Pods" + value = 1 + period_seconds = 15 + } + } + } + } +} diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 00000000..bca922c3 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,23 @@ +terraform { + backend "remote" { + hostname = "app.terraform.io" + organization = "7tv" + + workspaces { + prefix = "seventv-api-" + } + } +} + +locals { + infra_workspace_name = replace(terraform.workspace, "api", "infra") + infra = data.terraform_remote_state.infra.outputs + image_url = var.image_url != null ? var.image_url : format("ghcr.io/seventv/api:%s-latest", trimprefix(terraform.workspace, "seventv-api-")) + s3 = var.s3 != null ? var.s3 : { + region = local.infra.region + ak = local.infra.s3_access_key.id + sk = local.infra.s3_access_key.secret + internal_bucket = local.infra.s3_bucket.internal + public_bucket = local.infra.s3_bucket.public + } +} diff --git a/terraform/providers.tf b/terraform/providers.tf new file mode 100644 index 00000000..81c50587 --- /dev/null +++ b/terraform/providers.tf @@ -0,0 +1,35 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.7.0" + } + + kubernetes = { + source = "hashicorp/kubernetes" + version = "2.18.1" + } + + random = { + source = "hashicorp/random" + } + } +} + +provider "aws" { + region = var.region +} + +provider "kubernetes" { + host = data.aws_eks_cluster.cluster.endpoint + token = data.aws_eks_cluster_auth.cluster.token + cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority.0.data) +} + +data "aws_eks_cluster" "cluster" { + name = local.infra_workspace_name +} + +data "aws_eks_cluster_auth" "cluster" { + name = local.infra_workspace_name +} diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 00000000..90f7daab --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,94 @@ +data "terraform_remote_state" "infra" { + backend = "remote" + + config = { + organization = "7tv" + workspaces = { + name = local.infra_workspace_name + } + } +} + +variable "region" { + description = "AWS region" + type = string + default = "us-east-2" +} + +variable "namespace" { + type = string + default = "app" +} + +variable "image_url" { + type = string + nullable = true + default = null +} + +variable "image_pull_policy" { + type = string + default = "Always" +} + +variable "cdn_url" { + type = string +} + +variable "http_addr" { + type = string + default = "0.0.0.0" +} + +variable "http_port_gql" { + type = number + default = 3000 +} + +variable "http_port_rest" { + type = number + default = 3100 +} + +variable "twitch_client_id" { + type = string + default = "" +} + +variable "twitch_client_secret" { + type = string + default = "" +} + +variable "twitch_redirect_uri" { + type = string + default = "" +} + +variable "discord_client_id" { + type = string + default = "" +} + +variable "discord_client_secret" { + type = string + default = "" +} + +variable "discord_redirect_uri" { + type = string + default = "" +} + +variable "s3" { + type = object({ + endpoint = string + region = string + ak = string + sk = string + internal_bucket = string + public_bucket = string + }) + nullable = true + default = null +}