diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d54cfcb8..dcf94eea 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -6,6 +6,11 @@ on: - master - dev pull_request: + types: + - opened + - synchronize + - reopened + - labeled workflow_dispatch: inputs: deploy: @@ -42,6 +47,8 @@ jobs: - name: Checkout code uses: actions/checkout@v3 + with: + submodules: recursive - id: cache-paths run: | @@ -85,15 +92,12 @@ jobs: if: ${{ failure() }} steps: - - name: Cancel current workflow run - uses: actions/github-script@v6 + - name: Cancel Workflow + uses: potiuk/cancel-workflow-runs@master with: - script: | - github.actions.cancelWorkflowRun({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: context.runId - }) + cancelMode: self + notifyPRCancel: false + token: ${{ secrets.GITHUB_TOKEN }} build: name: Build and Test @@ -149,8 +153,8 @@ jobs: - lint 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') }} - if: ${{ (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy != 'none') || (github.event_name == 'push' && github.ref_type == 'branch' && (github.ref_name == 'master' || github.ref_name == 'dev')) }} + 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')) }} + if: ${{ (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy != 'none') || (github.event_name == 'push' && github.ref_type == 'branch' && (github.ref_name == 'master' || github.ref_name == 'dev')) || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'staged')) }} runs-on: self-hosted concurrency: group: ${{ github.workflow }}-deploy-${{ github.ref }} @@ -184,7 +188,7 @@ jobs: uses: docker/login-action@v2 with: registry: harbor.disembark.dev - username: robot$deploy-7tv + username: robot$ci password: ${{ secrets.REGISTRY_TOKEN }} - name: Install kubectl @@ -193,10 +197,18 @@ jobs: - name: "[Production] Tag & Push docker images" if: ${{ env.DEPLOY_PROD == 'true' }} run: | - docker tag api:${{ github.sha }} harbor.disembark.dev/7tv/api:latest - docker tag api:${{ github.sha }} harbor.disembark.dev/7tv/api:${{ github.sha }} - docker push harbor.disembark.dev/7tv/api:latest - docker push harbor.disembark.dev/7tv/api:${{ github.sha }} + docker tag api:${{ github.sha }} harbor.disembark.dev/production/api:latest + docker tag api:${{ github.sha }} harbor.disembark.dev/production/api:${{ github.sha }} + docker push harbor.disembark.dev/production/api:latest + docker push harbor.disembark.dev/production/api:${{ github.sha }} + + - name: "[Staging] Tag & Push docker images" + if: ${{ env.DEPLOY_STAGE == 'true' }} + run: | + docker tag api:${{ github.sha }} harbor.disembark.dev/staging/api:latest + docker tag api:${{ github.sha }} harbor.disembark.dev/staging/api:${{ github.sha }} + docker push harbor.disembark.dev/staging/api:latest + docker push harbor.disembark.dev/staging/api:${{ github.sha }} - name: "[Production] Update deployment template" uses: danielr1996/envsubst-action@1.1.0 @@ -204,27 +216,8 @@ jobs: env: IMAGE_TAG: ${{ github.sha }} with: - input: k8s/prod.template.yaml - output: k8s/prod.yaml - - - name: "[Production] Deploy to k8s" - if: ${{ env.DEPLOY_PROD == 'true' }} - env: - KUBE_CONFIG_DATA: ${{ secrets.KUBECONFIG }} - run: | - mkdir -p ~/.kube - (echo $KUBE_CONFIG_DATA | base64 -d) >> ~/.kube/config - kubectl config use-context autodeploy@SevenTV - - kubectl apply -f k8s/prod.yaml - - - name: "[Staging] Tag & Push docker images" - if: ${{ env.DEPLOY_STAGE == 'true' }} - run: | - docker tag api:${{ github.sha }} harbor.disembark.dev/7tv-stage/api:latest - docker tag api:${{ github.sha }} harbor.disembark.dev/7tv-stage/api:${{ github.sha }} - docker push harbor.disembark.dev/7tv-stage/api:latest - docker push harbor.disembark.dev/7tv-stage/api:${{ github.sha }} + input: k8s/production.template.yaml + output: k8s/out.yaml - name: "[Staging] Update deployment template" uses: danielr1996/envsubst-action@1.1.0 @@ -232,16 +225,14 @@ jobs: env: IMAGE_TAG: ${{ github.sha }} with: - input: k8s/dev.template.yaml - output: k8s/dev.yaml + input: k8s/staging.template.yaml + output: k8s/out.yaml - - name: "[Staging] Deploy to k8s" - if: ${{ env.DEPLOY_STAGE == 'true' }} + - name: Deploy to k8s env: - KUBE_CONFIG_DATA: ${{ secrets.KUBECONFIG_STAGE }} + KUBE_CONFIG_DATA: ${{ secrets.KUBECONFIG }} run: | mkdir -p ~/.kube (echo $KUBE_CONFIG_DATA | base64 -d) >> ~/.kube/config - kubectl config use-context autodeploy@SevenTV - kubectl apply -f k8s/dev.yaml + kubectl apply -f k8s/out.yaml diff --git a/Dockerfile b/Dockerfile index 50329437..3ea16bee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -53,6 +53,13 @@ FROM $BASE_IMG as go-builder FROM $BASE_IMG as final WORKDIR /app + RUN apt-get update && \ + apt-get install -y \ + ca-certificates && \ + apt-get autoremove -y && \ + apt-get clean -y && \ + rm -rf /var/cache/apt/archives /var/lib/apt/lists/* + COPY --from=go-builder /tmp/build/out . CMD ./api diff --git a/go.mod b/go.mod index 95a520a6..a557aeba 100644 --- a/go.mod +++ b/go.mod @@ -5,17 +5,17 @@ go 1.18 require ( github.com/99designs/gqlgen v0.17.5 github.com/SevenTV/Common v0.0.0-20220511225419-547f3a794ab7 - github.com/aws/aws-sdk-go v1.44.12 + github.com/aws/aws-sdk-go v1.44.17 github.com/bugsnag/panicwrap v1.3.4 github.com/golang-jwt/jwt/v4 v4.4.1 github.com/google/go-querystring v1.1.0 github.com/gorilla/websocket v1.5.0 github.com/json-iterator/go v1.1.12 github.com/nicklaw5/helix v1.25.0 - github.com/prometheus/client_golang v1.12.1 + github.com/prometheus/client_golang v1.12.2 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.11.0 - github.com/swaggo/swag v1.8.1 + github.com/swaggo/swag v1.8.2 go.mongodb.org/mongo-driver v1.9.1 go.uber.org/zap v1.21.0 ) @@ -50,7 +50,6 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.8.0 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect - golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect golang.org/x/tools v0.1.10 // indirect golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect google.golang.org/protobuf v1.28.0 // indirect @@ -81,15 +80,15 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/streadway/amqp v1.0.0 github.com/subosito/gotenv v1.2.0 // indirect - github.com/valyala/fasthttp v1.36.0 + github.com/valyala/fasthttp v1.37.0 github.com/vektah/gqlparser/v2 v2.4.2 github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.1 // indirect github.com/xdg-go/stringprep v1.0.3 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect + golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 // indirect + golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect + golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e // indirect golang.org/x/text v0.3.7 // indirect gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index bb2affd1..0f09eb75 100644 --- a/go.sum +++ b/go.sum @@ -44,7 +44,6 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/SevenTV/Common v0.0.0-20220511225419-547f3a794ab7 h1:z2YXvhAVUurJ9iPR2ddlwjgjP0GzMO1MoDiJRU5Fq9o= github.com/SevenTV/Common v0.0.0-20220511225419-547f3a794ab7/go.mod h1:k1+/uNULz0uSb6IPHI6G8HJutERV3IzF6i3gVk5hYT0= -github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs= 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= @@ -59,8 +58,8 @@ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= -github.com/aws/aws-sdk-go v1.44.12 h1:5f7ESFKQv5WHX8m37H2T8G+tc/rggy7sfdZ8ioqXFY8= -github.com/aws/aws-sdk-go v1.44.12/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.17 h1:of8MirZuVDat3BJgRbSwDO/GM/cgXh5Znf2tyEAv/vE= +github.com/aws/aws-sdk-go v1.44.17/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= 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 v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -287,7 +286,6 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= -github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= @@ -305,8 +303,9 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= +github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -360,8 +359,8 @@ github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMT github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/swaggo/swag v1.8.1 h1:JuARzFX1Z1njbCGz+ZytBR15TFJwF2Q7fu8puJHhQYI= -github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ= +github.com/swaggo/swag v1.8.2 h1:D4aBiVS2a65zhyk3WFqOUz7Rz0sOaUcgeErcid5uGL4= +github.com/swaggo/swag v1.8.2/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBnseg= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/urfave/cli/v2 v2.4.0/go.mod h1:NX9W0zmTvedE5oDoOMs2RTC8RvdK98NTYZE5LbaEYPg= @@ -370,8 +369,9 @@ github.com/urfave/cli/v2 v2.6.0/go.mod h1:oDzoM7pVwz6wHn5ogWgFUU1s4VJayeQS+aEZDq github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.33.0/go.mod h1:KJRK/MXx0J+yd0c5hlR+s1tIHD72sniU8ZJjl97LIw4= -github.com/valyala/fasthttp v1.36.0 h1:NhqfO/cB7Ajn1czkKnWkMHyPYr5nyND14ZGPk23g0/c= github.com/valyala/fasthttp v1.36.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= +github.com/valyala/fasthttp v1.37.0 h1:7WHCyI7EAkQMVmrfBhWTCOaeROb1aCBiTopx63LkMbE= +github.com/valyala/fasthttp v1.37.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vektah/gqlparser/v2 v2.4.2 h1:29TGc6QmhEUq5fll+2FPoTmhUhR65WEKN4VK/jo0OlM= github.com/vektah/gqlparser/v2 v2.4.2/go.mod h1:flJWIR04IMQPGz+BXLrORkrARBxv/rtyIAFvd/MceW0= @@ -423,8 +423,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 h1:Tgea0cVUD0ivh5ADBX4WwuI12DUd2to3nCYe2eayMIw= -golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0= +golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -503,7 +503,6 @@ golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -525,8 +524,9 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4= +golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -581,8 +581,8 @@ golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e h1:w36l2Uw3dRan1K3TyXriXvY+6T56GNmlKGcqiQUJDfM= +golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/internal/configure/config.go b/internal/configure/config.go index 22f61f96..30c15d50 100644 --- a/internal/configure/config.go +++ b/internal/configure/config.go @@ -109,10 +109,14 @@ type Config struct { NoHeader bool `mapstructure:"noheader" json:"noheader"` WebsiteURL string `mapstructure:"website_url" json:"website_url"` OldWebsiteURL string `mapstructure:"website_old_url" json:"website_old_url"` - NodeName string `mapstructure:"node_name" json:"node_name"` TempFolder string `mapstructure:"temp_folder" json:"temp_folder"` CdnURL string `mapstructure:"cdn_url" json:"cdn_url"` + K8S struct { + NodeName string `mapstructure:"node_name" json:"node_name"` + PodName string `mapstructure:"pod_name" json:"pod_name"` + } `mapstructure:"k8s" json:"k8s"` + Redis struct { Username string `mapstructure:"username" json:"username"` Password string `mapstructure:"password" json:"password"` @@ -129,17 +133,18 @@ type Config struct { } `mapstructure:"mongo" json:"mongo"` Health struct { - Enabled bool - Bind string - } + Enabled bool `mapstructure:"enabled" json:"enabled"` + Bind string `mapstructure:"bind" json:"bind"` + } `mapstructure:"health" json:"health"` Monitoring struct { - Enabled bool - Bind string - Labels Labels - } + Enabled bool `mapstructure:"enabled" json:"enabled"` + Bind string `mapstructure:"bind" json:"bind"` + Labels Labels `mapstructure:"labels" json:"labels"` + } `mapstructure:"monitoring" json:"monitoring"` Http struct { + Type string `mapstructure:"type" json:"type"` Addr string `mapstructure:"uri" json:"uri"` VersionSuffix string `mapstructure:"version_suffix" json:"version_suffix"` Ports struct { @@ -147,7 +152,6 @@ type Config struct { REST int `mapstructure:"rest" json:"rest"` } `mapstructure:"ports" json:"ports"` - Type string `mapstructure:"type" json:"type"` OauthRedirectURI string `mapstructure:"oauth_redirect_uri" json:"oauth_redirect_uri"` Cookie struct { Domain string `mapstructure:"cookie_domain" json:"cookie_domain"` diff --git a/internal/externalapis/twitch.go b/internal/externalapis/twitch.go index 32dd5654..c3e4edec 100644 --- a/internal/externalapis/twitch.go +++ b/internal/externalapis/twitch.go @@ -2,6 +2,7 @@ package externalapis import ( "fmt" + "io/ioutil" "net/http" "github.com/nicklaw5/helix" @@ -26,6 +27,15 @@ func (twitch) GetUserFromToken(gCtx global.Context, token string) ([]helix.User, } defer resp.Body.Close() + if resp.StatusCode != 200 { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return nil, fmt.Errorf("bad resp from twitch: %d - %s", resp.StatusCode, body) + } + var res helix.ManyUsers if err = ReadRequestResponse(resp, &res); err != nil { return nil, err diff --git a/internal/gql/gql.go b/internal/gql/gql.go index 71ab7f27..6f4a3176 100644 --- a/internal/gql/gql.go +++ b/internal/gql/gql.go @@ -1,11 +1,9 @@ package gql import ( - "encoding/json" "fmt" "time" - "github.com/SevenTV/Common/errors" "github.com/SevenTV/Common/utils" "github.com/fasthttp/router" "github.com/seventv/api/internal/global" @@ -29,31 +27,21 @@ func New(gCtx global.Context) error { router := router.New() router.RedirectTrailingSlash = true - mid := func(ctx *fasthttp.RequestCtx) { + v3Route := func(ctx *fasthttp.RequestCtx) { if err := middleware.Auth(gCtx)(ctx); err != nil { ctx.Response.Header.Add("X-Auth-Failure", err.Message()) - goto handler } + gqlv3(ctx) + } - handler: - switch ctx.UserValue("v") { - case "v3": - gqlv3(ctx) - case "v2": - gqlv2(ctx) - default: - err := errors.ErrUnknownRoute() - b, _ := json.Marshal(map[string]interface{}{ - "error": err.Message(), - "error_code": err.Code(), - }) - _, _ = ctx.Write(b) - ctx.SetContentType("application/json") - ctx.SetStatusCode(fasthttp.StatusNotFound) + router.GET(fmt.Sprintf("/v3%s/gql", gCtx.Config().Http.VersionSuffix), v3Route) + router.POST(fmt.Sprintf("/v3%s/gql", gCtx.Config().Http.VersionSuffix), v3Route) + router.POST(fmt.Sprintf("/v2%s/gql", gCtx.Config().Http.VersionSuffix), func(ctx *fasthttp.RequestCtx) { + if err := middleware.Auth(gCtx)(ctx); err != nil { + ctx.Response.Header.Add("X-Auth-Failure", err.Message()) } - } - router.GET("/{v}", mid) - router.POST("/{v}", mid) + gqlv2(ctx) + }) router.HandleOPTIONS = true server := fasthttp.Server{ @@ -61,24 +49,24 @@ func New(gCtx global.Context) error { start := time.Now() defer func() { if err := recover(); err != nil { - zap.S().Errorw("panic in request handler", + zap.S().Errorw("panic in gql request handler", "panic", err, "status", ctx.Response.StatusCode(), "duration", time.Since(start)/time.Millisecond, "method", utils.B2S(ctx.Method()), "path", utils.B2S(ctx.Path()), - "ip", utils.B2S(ctx.Response.Header.Peek("Cf-Connecting-IP")), - "origin", utils.B2S(ctx.Response.Header.Peek("Origin")), + "ip", utils.B2S(ctx.Request.Header.Peek("Cf-Connecting-IP")), + "origin", utils.B2S(ctx.Request.Header.Peek("Origin")), ) ctx.Response.SetStatusCode(fasthttp.StatusInternalServerError) } else { - zap.S().Infow("request", + zap.S().Infow("gql request", "status", ctx.Response.StatusCode(), "duration", time.Since(start)/time.Millisecond, "method", utils.B2S(ctx.Method()), "path", utils.B2S(ctx.Path()), - "ip", utils.B2S(ctx.Response.Header.Peek("Cf-Connecting-IP")), - "origin", utils.B2S(ctx.Response.Header.Peek("Origin")), + "ip", utils.B2S(ctx.Request.Header.Peek("Cf-Connecting-IP")), + "origin", utils.B2S(ctx.Request.Header.Peek("Origin")), ) } }() @@ -88,6 +76,9 @@ func New(gCtx global.Context) error { ctx.Response.Header.Set("Access-Control-Expose-Headers", "X-Collection-Size") ctx.Response.Header.Set("Access-Control-Allow-Methods", "*") ctx.Response.Header.Set("Access-Control-Allow-Origin", "*") + + ctx.Response.Header.Set("X-Node-Name", gCtx.Config().K8S.NodeName) + ctx.Response.Header.Set("X-Pod-Name", gCtx.Config().K8S.PodName) if ctx.IsOptions() { ctx.SetStatusCode(fasthttp.StatusNoContent) return diff --git a/internal/rest/rest.go b/internal/rest/rest.go index 2609e39f..87de6e69 100644 --- a/internal/rest/rest.go +++ b/internal/rest/rest.go @@ -43,23 +43,23 @@ func New(gCtx global.Context) error { start := time.Now() defer func() { if err := recover(); err != nil { - zap.S().Errorw("panic in request handler", + zap.S().Errorw("panic in rest request handler", "panic", err, "status", ctx.Response.StatusCode(), "duration", time.Since(start)/time.Millisecond, "method", utils.B2S(ctx.Method()), "path", utils.B2S(ctx.Path()), - "ip", utils.B2S(ctx.Response.Header.Peek("Cf-Connecting-IP")), - "origin", utils.B2S(ctx.Response.Header.Peek("Origin")), + "ip", utils.B2S(ctx.Request.Header.Peek("Cf-Connecting-IP")), + "origin", utils.B2S(ctx.Request.Header.Peek("Origin")), ) } else { - zap.S().Infow("request", + zap.S().Infow("rest request", "status", ctx.Response.StatusCode(), "duration", time.Since(start)/time.Millisecond, "method", utils.B2S(ctx.Method()), "path", utils.B2S(ctx.Path()), - "ip", utils.B2S(ctx.Response.Header.Peek("Cf-Connecting-IP")), - "origin", utils.B2S(ctx.Response.Header.Peek("Origin")), + "ip", utils.B2S(ctx.Request.Header.Peek("Cf-Connecting-IP")), + "origin", utils.B2S(ctx.Request.Header.Peek("Origin")), ) } }() @@ -69,6 +69,9 @@ func New(gCtx global.Context) error { ctx.Response.Header.Set("Access-Control-Allow-Headers", "*") ctx.Response.Header.Set("Access-Control-Allow-Methods", "*") ctx.Response.Header.Set("Access-Control-Allow-Origin", "*") + + ctx.Response.Header.Set("X-Node-Name", gCtx.Config().K8S.NodeName) + ctx.Response.Header.Set("X-Pod-Name", gCtx.Config().K8S.PodName) if ctx.IsOptions() { return } diff --git a/internal/rest/v2/routes/root.go b/internal/rest/v2/routes/root.go index 90ae7043..f886450b 100644 --- a/internal/rest/v2/routes/root.go +++ b/internal/rest/v2/routes/root.go @@ -20,7 +20,7 @@ func New(gCtx global.Context) rest.Route { func (r *Route) Config() rest.RouteConfig { return rest.RouteConfig{ - URI: "/v2", + URI: "/v2" + r.Ctx.Config().Http.VersionSuffix, Method: rest.GET, Children: []rest.Route{ auth.New(r.Ctx), diff --git a/internal/rest/v3/routes/auth/twitch.cb.auth.go b/internal/rest/v3/routes/auth/twitch.cb.auth.go index 2268cd1e..1be24ba9 100644 --- a/internal/rest/v3/routes/auth/twitch.cb.auth.go +++ b/internal/rest/v3/routes/auth/twitch.cb.auth.go @@ -3,6 +3,7 @@ package auth import ( "encoding/json" "fmt" + "io/ioutil" "net/http" "strings" "time" @@ -147,6 +148,23 @@ func (r *twitchCallback) Handler(ctx *rest.Ctx) rest.APIError { } defer resp.Body.Close() + if resp.StatusCode != 200 { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + zap.S().Errorw("twitch", + "error", err, + ) + + return errors.ErrInternalServerError().SetDetail("Internal Request Rejected by External Provider") + } + + zap.S().Errorw("twitch", + "error", fmt.Errorf("bad resp from twitch: %d - %s", resp.StatusCode, body), + ) + + return errors.ErrInternalServerError().SetDetail("Internal Request Rejected by External Provider") + } + grant := &OAuth2AuthorizedResponse{} if err = externalapis.ReadRequestResponse(resp, grant); err != nil { zap.S().Errorw("ReadRequestResponse", diff --git a/internal/rest/v3/routes/root.go b/internal/rest/v3/routes/root.go index a96aef15..cf316f4f 100644 --- a/internal/rest/v3/routes/root.go +++ b/internal/rest/v3/routes/root.go @@ -19,7 +19,7 @@ func New(gCtx global.Context) rest.Route { func (r *Route) Config() rest.RouteConfig { return rest.RouteConfig{ - URI: "/v3", + URI: "/v3" + r.Ctx.Config().Http.VersionSuffix, Method: rest.GET, Children: []rest.Route{ docs.New(r.Ctx), diff --git a/k8s/.gitignore b/k8s/.gitignore new file mode 100644 index 00000000..e0cc83b4 --- /dev/null +++ b/k8s/.gitignore @@ -0,0 +1 @@ +!*.template.yaml diff --git a/k8s/staging.template.yaml b/k8s/staging.template.yaml new file mode 100644 index 00000000..1857f454 --- /dev/null +++ b/k8s/staging.template.yaml @@ -0,0 +1,196 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: api + namespace: app-staging +spec: + selector: + matchLabels: + app: api + template: + metadata: + labels: + app: api + spec: + volumes: + - name: config + configMap: + name: api-config + defaultMode: 420 + containers: + - name: api + image: harbor.disembark.dev/staging/api:${IMAGE_TAG} + ports: + - name: gql + containerPort: 3000 + protocol: TCP + - name: rest + containerPort: 3100 + protocol: TCP + - name: metrics + containerPort: 9100 + protocol: TCP + - name: health + containerPort: 9200 + protocol: TCP + envFrom: + - secretRef: + name: api-secret + env: + - name: API_K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: API_K8S_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + resources: + limits: + cpu: "4" + memory: 2Gi + requests: + cpu: 500m + memory: 128Mi + volumeMounts: + - name: config + mountPath: /app/config.yaml + subPath: config.yaml + livenessProbe: + tcpSocket: + port: health + initialDelaySeconds: 30 + timeoutSeconds: 5 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 6 + readinessProbe: + tcpSocket: + port: health + initialDelaySeconds: 5 + timeoutSeconds: 5 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 6 + imagePullPolicy: Always + imagePullSecrets: + - name: regcred +--- +apiVersion: v1 +kind: Service +metadata: + name: api + namespace: app-staging + labels: + app: api +spec: + ports: + - name: gql + protocol: TCP + port: 3000 + targetPort: gql + - name: rest + protocol: TCP + port: 3100 + targetPort: rest + - name: metrics + protocol: TCP + port: 9100 + targetPort: metrics + - name: health + protocol: TCP + port: 9200 + targetPort: health + selector: + app: api +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: api-config + namespace: app-staging +data: + config.yaml: | + level: info + + temp_folder: tmp + + website_url: https://7tv.dev/ + website_old_url: https://old.7tv.dev/ + cdn_url: cdn.7tv.app + + redis: + master_name: mymaster + database: 0 + username: default + addresses: + - redis-main-headless.redis-main.svc.cluster.local:26379 + sentinel: true + + mongo: + db: 7tv_v3_stage_test3 + + http: + addr: "0.0.0.0" + ports: + gql: 3000 + rest: 3100 + type: tcp + version_suffix: "-stage" + + quota_default_limit: 1000 + quota_max_bad_queries: 5 + + cookie_domain: "7tv.io" + cookie_secure: true + + health: + enabled: true + bind: 0.0.0.0:9200 + + metrics: + enabled: true + bind: 0.0.0.0:9100 + + rmq: + job_queue_name: "jobs_stage" + result_queue_name: "results_stage" + update_queue_name: "updates_stage" + + aws: + region: "us-east-1" + internal_bucket: "cdn-e06f66b8-426e-45a5-9eeb-486360ccee00" + public_bucket: "cdn-e06f66b8-426e-45a5-9eeb-486360ccee00" + endpoint: http://rook-ceph-rgw-ceph-objectstore.rook-ceph.svc.cluster.local + + platforms: + twitch: + enabled: true + client_id: "wglrcd55waquikrkatyigdg1vqvmws" + redirect_uri: "https://7tv.io/v3-stage/auth/twitch/callback" +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: api + namespace: app-staging +spec: + entryPoints: + - https + routes: + - kind: Rule + match: Host(`7tv.io`) && (PathPrefix(`/v3-stage/gql`) || PathPrefix(`/v2-stage/gql`)) + services: + - kind: Service + name: api + namespace: app-staging + port: gql + - kind: Rule + match: Host(`7tv.io`) && (PathPrefix(`/v3-stage`) || PathPrefix(`/v2-stage`)) + services: + - kind: Service + name: api + namespace: app-staging + port: rest + tls: + secretName: 7tv-io-cert