├── .github ├── CODE_OF_CONDUCT.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── go-function-offline-testing.yml │ ├── python-function-offline-testing.yml │ └── triggers-functions-offline-testing.yml ├── .gitignore ├── README.md ├── containers ├── bash-scheduled-job │ ├── README.md │ ├── app │ │ ├── Dockerfile │ │ ├── script.sh │ │ └── server.sh │ ├── package.json │ └── serverless.yml ├── csharp-hello-world │ ├── Dockerfile │ ├── HelloWorldApp.csproj │ ├── Program.cs │ └── README.md ├── function-handler-java │ ├── .gitignore │ ├── README.md │ ├── java-container │ │ ├── Dockerfile │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ └── java │ │ │ └── Handler.java │ ├── package.json │ └── serverless.yml ├── grpc-http2-go │ ├── Dockerfile │ ├── README.md │ ├── client │ │ └── client.go │ ├── go.mod │ ├── go.sum │ ├── hello.pb.go │ ├── hello.proto │ ├── hello_grpc.pb.go │ └── server │ │ └── main.go ├── memos-terraform │ ├── README.md │ └── main.tf ├── nginx-cors-private-python │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── curl.tftpl │ ├── docker-compose.yml │ ├── gateway │ │ ├── Dockerfile │ │ └── conf.template │ ├── index.tftpl │ ├── server │ │ ├── Dockerfile │ │ ├── dummy.png │ │ ├── hello.png │ │ ├── requirements.txt │ │ └── server.py │ └── terraform │ │ ├── .gitignore │ │ ├── main.tf │ │ └── vars │ │ └── main.tfvars ├── nginx-hello-world │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── serverless.yml ├── python-hello-world │ ├── README.md │ ├── container │ │ ├── Dockerfile │ │ ├── handler.py │ │ └── requirements.txt │ ├── package-lock.json │ ├── package.json │ └── serverless.yml ├── python-s3-upload │ ├── README.md │ ├── container │ │ ├── Dockerfile │ │ ├── app.py │ │ └── requirements.txt │ └── terraform │ │ ├── container.tf │ │ ├── image.tf │ │ ├── outputs.tf │ │ ├── providers.tf │ │ ├── s3.tf │ │ ├── variables.tf │ │ └── versions.tf ├── ruby-hello-world │ ├── Dockerfile │ ├── Gemfile │ ├── README.md │ └── app.rb ├── rust-hello-world │ ├── Cargo.lock │ ├── Cargo.toml │ ├── Dockerfile │ ├── README.md │ └── src │ │ └── main.rs ├── terraform-nginx-hello-world │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── providers.tf │ ├── variables.tf │ └── versions.tf └── terraform-triggers │ ├── .gitignore │ ├── README.md │ ├── docker.tf │ ├── docker │ ├── go │ │ ├── Dockerfile │ │ ├── go.mod │ │ └── server.go │ └── python │ │ ├── Dockerfile │ │ ├── requirements.txt │ │ └── server.py │ ├── mnq_nats.tf │ ├── mnq_sqs.tf │ ├── outputs.tf │ ├── provider.tf │ ├── serverless_containers.tf │ ├── tests │ ├── .gitignore │ ├── requirements.txt │ └── send_messages.py │ ├── triggers.tf │ ├── variables.tf │ └── versions.tf ├── docs └── templates │ └── readme-example-template.md ├── functions ├── badge-php │ ├── .gitignore │ ├── README.md │ ├── composer.json │ ├── handler.php │ ├── package.json │ ├── serverless.yml │ └── views │ │ └── badge.twig ├── cors-go │ ├── .gitignore │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── handler.go │ ├── handler_test.go │ ├── package.json │ ├── serverless.yml │ └── test │ │ └── main.go ├── cors-node │ ├── .gitignore │ ├── README.md │ ├── handler.js │ ├── package.json │ └── serverless.yml ├── cors-python │ ├── .gitignore │ ├── README.md │ ├── handler.py │ ├── package.json │ ├── requirements-dev.txt │ ├── serverless.yml │ └── test │ │ └── cors_python_test.py ├── cors-rust │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── package.json │ ├── serverless.yml │ └── src │ │ └── lib.rs ├── go-hello-world │ ├── .gitignore │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── handler.go │ ├── handler_test.go │ ├── package-lock.json │ ├── package.json │ ├── serverless.yml │ └── test │ │ └── main.go ├── go-mail │ ├── .env │ ├── .gitignore │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── handler.go │ ├── package.json │ └── serverless.yml ├── go-mnq-sqs-publish │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── handler.go │ ├── handler_test.go │ ├── package-lock.json │ ├── package.json │ ├── serverless.yml │ └── test │ │ └── main.go ├── go-mongo │ ├── README.md │ ├── cmd │ │ └── main.go │ ├── go.mod │ ├── go.sum │ └── main.go ├── go-upload-file-s3-multipart │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── handler.go │ ├── handler_test.go │ └── test │ │ └── main.go ├── image-labelling-node │ ├── .gitignore │ ├── README.md │ ├── handler.js │ ├── package.json │ └── serverless.yml ├── image-transform-node │ ├── .gitignore │ ├── BucketScan.js │ ├── ImageTransform.js │ ├── README.md │ ├── package.json │ └── serverless.yml ├── node-terraform │ ├── .gitignore │ ├── README.md │ ├── function │ │ ├── index.js │ │ ├── package-lock.json │ │ └── package.json │ ├── main.tf │ └── provider.tf ├── node-upload-file-s3-multipart │ ├── .gitignore │ ├── README.md │ ├── handler.js │ ├── package.json │ └── serverless.yml ├── php-s3 │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── function │ │ ├── composer.json │ │ ├── composer.lock │ │ └── handler.php │ └── terraform │ │ └── main.tf ├── postgre-sql-node │ ├── .gitignore │ ├── README.md │ ├── handler.js │ ├── package.json │ └── serverless.yml ├── postgre-sql-python │ ├── README.md │ ├── bin │ │ ├── deploy.sh │ │ └── deps.sh │ ├── handlers │ │ └── handler.py │ ├── package.json │ ├── requirements-dev.txt │ ├── requirements.txt │ └── serverless.yml ├── python-chatbot │ ├── .gitignore │ ├── README.md │ ├── app │ │ ├── chatbot.py │ │ ├── english-corpus.sqlite3 │ │ └── templates │ │ │ └── index.html │ ├── bin │ │ ├── deploy.sh │ │ └── deps.sh │ ├── package.json │ ├── requirements-dev.txt │ ├── requirements.txt │ └── serverless.yml ├── python-dependencies │ ├── .gitignore │ ├── README.md │ ├── bin │ │ ├── deploy.sh │ │ └── deps.sh │ ├── handlers │ │ └── handler.py │ ├── package-lock.json │ ├── package.json │ ├── requirements-dev.txt │ ├── requirements.txt │ └── serverless.yml ├── python-sqs-trigger-async-worker │ ├── README.md │ ├── function │ │ ├── handler.py │ │ └── requirements.txt │ └── main.tf ├── python-sqs-trigger-hello-world │ ├── README.md │ ├── handler.py │ └── main.tf ├── python-tem-smtp-server │ ├── README.md │ ├── handler.py │ └── main.tf ├── python-upload-file-s3-multipart │ ├── README.md │ ├── app.py │ ├── requirements-dev.txt │ └── requirements.txt ├── redis-tls │ ├── .gitignore │ ├── README.md │ ├── requirements.txt │ ├── terraform │ │ ├── main.tf │ │ ├── provider.tf │ │ ├── store.tf │ │ └── variables.tf │ └── weather_to_redis.py ├── rust-mnist │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── common │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── serverless.yaml │ ├── src │ │ ├── bin │ │ │ └── test-server.rs │ │ └── lib.rs │ ├── training │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ └── www │ │ ├── .gitignore │ │ ├── index.html │ │ ├── package.json │ │ ├── pnpm-lock.yaml │ │ ├── public │ │ └── sls.svg │ │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── Drawing.tsx │ │ ├── assets │ │ │ └── react.svg │ │ ├── env.d.ts │ │ ├── index.css │ │ ├── main.tsx │ │ └── vite-env.d.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.node.json │ │ └── vite.config.ts ├── secret-manager-rotate-secret │ ├── .gitignore │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── handler.go │ ├── package.json │ ├── random │ │ └── string.go │ └── serverless.yml ├── serverless-gateway-python │ ├── README.md │ ├── app.py │ ├── requirements-dev.txt │ ├── requirements.txt │ └── scripts │ │ ├── add_function_to_gateway.sh │ │ ├── delete_function_from_gateway.sh │ │ └── list_gateway_endpoints.sh ├── terraform-python-example │ ├── README.md │ ├── function │ │ └── handler.py │ ├── main.tf │ ├── provider.tf │ ├── terraform.tfvars │ └── variables.tf ├── trigger-image-transform-node │ ├── .DS_Store │ ├── .gitignore │ ├── BucketScan.js │ ├── ImageTransform.js │ ├── README.md │ ├── package.json │ └── serverless.yml ├── triggers-getting-started │ ├── .gitignore │ ├── .terraform.lock.hcl │ ├── README.md │ ├── go │ │ ├── cmd │ │ │ └── main.go │ │ ├── go.mod │ │ ├── go.sum │ │ └── handler.go │ ├── main.tf │ ├── nats.tf │ ├── node │ │ ├── .gitignore │ │ ├── handler.js │ │ ├── package-lock.json │ │ └── package.json │ ├── outputs.tf │ ├── php │ │ └── handler.php │ ├── provider.tf │ ├── python │ │ ├── handler.py │ │ └── requirements-dev.txt │ ├── rust │ │ ├── .gitignore │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── sqs.tf │ └── tests │ │ ├── requirements.txt │ │ ├── send_nats_messages.py │ │ ├── send_sqs_messages.py │ │ └── test_handler.py ├── triggers-nats │ ├── .gitignore │ ├── README.md │ ├── files │ │ └── function.zip │ ├── function │ │ └── handler.py │ ├── main.tf │ ├── messaging.tf │ ├── outputs.tf │ ├── provider.tf │ └── tests │ │ ├── requirements.txt │ │ └── send_messages.py └── typescript-with-node │ ├── .gitignore │ ├── README.md │ ├── handler.ts │ ├── package.json │ └── serverless.yml ├── jobs ├── instances-snapshot-cleaner │ ├── Dockerfile │ ├── README.md │ ├── go.mod │ ├── go.sum │ └── main.go ├── instances-snapshot │ ├── Dockerfile │ ├── README.md │ ├── go.mod │ ├── go.sum │ └── main.go ├── ml-ops │ ├── README.md │ ├── data │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── main.py │ │ └── requirements.txt │ ├── docker-compose.yml │ ├── inference │ │ ├── Dockerfile │ │ ├── data.py │ │ ├── example.json │ │ ├── loader.py │ │ ├── main.py │ │ └── requirements.txt │ ├── terraform │ │ ├── container.tf │ │ ├── images.tf │ │ ├── jobs.tf │ │ ├── outputs.tf │ │ ├── providers.tf │ │ ├── s3.tf │ │ ├── utils.tf │ │ ├── variables.tf │ │ └── versions.tf │ └── training │ │ ├── Dockerfile │ │ ├── main.py │ │ ├── requirements.txt │ │ └── training.py ├── registry-empty-ressource-cleaner │ ├── Dockerfile │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── main.go │ └── registry.go ├── registry-version-based-retention │ ├── Dockerfile │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── main.go │ └── registry.go └── terraform-hello-world │ ├── README.md │ ├── image │ ├── Dockerfile │ └── hello.sh │ └── terraform │ ├── images.tf │ ├── jobs.tf │ ├── outputs.tf │ ├── providers.tf │ ├── variables.tf │ └── versions.tf ├── mnq ├── large-messages │ ├── README.md │ ├── function │ │ ├── handler │ │ │ └── large_messages.py │ │ └── requirements.txt │ ├── main.tf │ └── upload_img.sh ├── serverless-scraping │ ├── .gitignore │ ├── README.md │ ├── archives │ │ └── .gitignore │ ├── consumer │ │ ├── handlers │ │ │ └── consumer.py │ │ └── requirements.txt │ ├── scraper │ │ ├── handlers │ │ │ └── scrape_hn.py │ │ └── requirements.txt │ └── terraform │ │ └── main.tf └── sns-instances-notification-system │ ├── README.md │ ├── publisher-server │ ├── Dockerfile │ ├── cmd │ │ └── publisher │ │ │ └── main.go │ ├── go.mod │ ├── go.sum │ ├── internal │ │ ├── handlers │ │ │ ├── high_cpu_usage.go │ │ │ └── low_cpu_usage.go │ │ ├── sns_client │ │ │ └── client.go │ │ └── utils │ │ │ └── utils.go │ └── www │ │ ├── index.html │ │ └── templates │ │ └── message.html │ ├── subscriber-server │ ├── Dockerfile │ ├── cmd │ │ └── subscriber │ │ │ └── main.go │ ├── go.mod │ ├── go.sum │ ├── internal │ │ ├── app │ │ │ └── app_state.go │ │ ├── handlers │ │ │ ├── common.go │ │ │ ├── notifications.go │ │ │ ├── sns_service.go │ │ │ └── subscription.go │ │ ├── types │ │ │ ├── confirmation.go │ │ │ └── notification.go │ │ └── utils │ │ │ └── utils.go │ └── www │ │ ├── index.html │ │ └── templates │ │ ├── confirm-subscription.html │ │ ├── message.html │ │ └── notifications.html │ └── terraform │ ├── main.tf │ └── variables.tf └── projects ├── blogpost-glacier ├── README.md ├── caas │ ├── .env │ ├── my-container │ │ ├── Dockerfile │ │ ├── go.mod │ │ ├── go.sum │ │ ├── layout.html │ │ └── main.go │ ├── package-lock.json │ ├── package.json │ └── serverless.yml ├── faas │ ├── .env │ ├── go.mod │ ├── go.sum │ ├── handler.go │ ├── package-lock.json │ ├── package.json │ └── serverless.yml ├── package-lock.json ├── package.json └── serverless-compose.yml ├── kong-api-gateway ├── Makefile ├── README.md ├── apiGateway │ ├── kong │ │ ├── Dockerfile │ │ ├── kong.conf │ │ ├── kong.yml.template │ │ └── start.sh │ └── serverless.yml ├── getToken │ ├── deploy.sh │ ├── info.sh │ ├── plugin.js │ ├── remove.sh │ ├── serverless.yml │ └── token ├── myApp │ ├── commands.py │ ├── orders.py │ └── serverless.yml ├── package.json └── serverless-compose.yml └── tutorial-sdb-nextjs-terraform ├── .dockerignore ├── Dockerfile ├── README.md ├── license ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── assets │ └── blog │ │ ├── authors │ │ ├── jj.jpeg │ │ ├── joe.jpeg │ │ └── tim.jpeg │ │ ├── dynamic-routing │ │ └── cover.jpg │ │ ├── hello-world │ │ └── cover.jpg │ │ └── preview │ │ └── cover.jpg └── favicon │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── mstile-150x150.png │ ├── safari-pinned-tab.svg │ └── site.webmanifest ├── src ├── app │ ├── _components │ │ ├── alert.tsx │ │ ├── avatar.tsx │ │ ├── container.tsx │ │ ├── cover-image.tsx │ │ ├── date-formatter.tsx │ │ ├── footer.tsx │ │ ├── header.tsx │ │ ├── hero-post.tsx │ │ ├── intro.tsx │ │ ├── markdown-styles.module.css │ │ ├── more-stories.tsx │ │ ├── post-body.tsx │ │ ├── post-header.tsx │ │ ├── post-preview.tsx │ │ ├── post-title.tsx │ │ └── section-separator.tsx │ ├── globals.css │ ├── layout.tsx │ ├── page.tsx │ └── posts │ │ └── [slug] │ │ └── page.tsx ├── interfaces │ ├── author.ts │ └── post.ts ├── lib │ ├── api.ts │ ├── constants.ts │ ├── markdownToHtml.ts │ └── utils.ts └── public │ ├── assets │ └── blog │ │ ├── authors │ │ ├── jj.jpeg │ │ ├── joe.jpeg │ │ └── tim.jpeg │ │ ├── dynamic-routing │ │ └── cover.jpg │ │ ├── hello-world │ │ └── cover.jpg │ │ └── preview │ │ └── cover.jpg │ └── favicon │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── mstile-150x150.png │ ├── safari-pinned-tab.svg │ └── site.webmanifest ├── tailwind.config.ts └── tsconfig.json /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | 3 | ## Checklist 4 | 5 | - [ ] I have reviewed this myself. 6 | - [ ] I have attached a README to my example. You can use [this template](../docs/templates/readme-example-template.md) as reference. 7 | - [ ] I have updated the project README to link my example. 8 | 9 | ## Details 10 | -------------------------------------------------------------------------------- /.github/workflows/go-function-offline-testing.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: golang-function-testing 3 | 4 | on: 5 | push: 6 | branches: [main] 7 | pull_request: 8 | branches: [main] 9 | 10 | jobs: 11 | go-example: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | tests: [ 16 | { directory: 'cors-go', go-version: '1.22' }, 17 | { directory: 'go-hello-world', go-version: '1.20' }, 18 | { directory: 'go-mnq-sqs-publish', go-version: '1.21' }, 19 | { directory: 'go-upload-file-s3-multipart', go-version: '1.20' } 20 | ] 21 | 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | 26 | - name: Setup Go ${{ matrix.tests.go-version }} 27 | uses: actions/setup-go@v3 28 | with: 29 | go-version: ${{ matrix.tests.go-version}} 30 | 31 | - name: Run test for ${{ matrix.tests.directory }} 32 | working-directory: functions/${{ matrix.tests.directory }} 33 | run: | 34 | go run test/main.go & 35 | go test ./... 36 | env: 37 | # for go-mnq-sqs-publish example 38 | SQS_ACCESS_KEY: ${{ secrets.SQS_ACCESS_KEY }} 39 | SQS_SECRET_KEY: ${{ secrets.SQS_SECRET_KEY }} 40 | 41 | # for go-upload-file-s3-multipart example 42 | S3_ENABLED: false 43 | -------------------------------------------------------------------------------- /.github/workflows/python-function-offline-testing.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: python-function-offline-testing 3 | 4 | on: 5 | push: 6 | branches: [main] 7 | pull_request: 8 | branches: [main] 9 | 10 | jobs: 11 | cors-python: 12 | runs-on: ubuntu-22.04 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | - name: Python setup 17 | uses: actions/setup-python@v4 18 | with: 19 | python-version: "3.11" 20 | - name: Install python dev requirements 21 | run: pip install -r functions/cors-python/requirements-dev.txt 22 | - name: Run python offline testing 23 | uses: BerniWittmann/background-server-action@v1 24 | with: 25 | command: pytest functions/cors-python 26 | start: | 27 | cd functions/cors-python 28 | python functions/cors-python/handler.py 29 | wait-on: 'http://localhost:8080' 30 | wait-on-timeout: 60 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Terraform 2 | **/.terraform/* 3 | 4 | *.tfstate 5 | *.tfstate.* 6 | *.tfvars 7 | 8 | .terraform.lock.hcl 9 | 10 | # Serverless framework 11 | node_modules/ 12 | .serverless/ 13 | 14 | # Env files 15 | *.env 16 | 17 | # Python 18 | venv/ 19 | __pycache__/ 20 | 21 | # Python API framework 22 | package/ 23 | .scw 24 | -------------------------------------------------------------------------------- /containers/bash-scheduled-job/app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | WORKDIR /usr/src/app 3 | 4 | ENV PORT 8080 5 | EXPOSE 8080 6 | 7 | RUN apk update && apk add --no-cache netcat-openbsd bash 8 | COPY server.sh . 9 | COPY script.sh . 10 | 11 | CMD ["/bin/bash", "./server.sh"] 12 | -------------------------------------------------------------------------------- /containers/bash-scheduled-job/app/script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Do your scheduled job here 4 | 5 | # Print to container logs 6 | echo "Scheduled job executed at $(date)." 7 | -------------------------------------------------------------------------------- /containers/bash-scheduled-job/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "container-bash-starter", 3 | "version": "1.0.0", 4 | "keywords": [], 5 | "author": "", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "serverless-scaleway-functions": "^0.4.5" 9 | }, 10 | "description": "" 11 | } 12 | -------------------------------------------------------------------------------- /containers/bash-scheduled-job/serverless.yml: -------------------------------------------------------------------------------- 1 | service: scaleway-container 2 | 3 | provider: 4 | name: scaleway 5 | 6 | plugins: 7 | - serverless-scaleway-functions 8 | 9 | package: 10 | patterns: 11 | - '!node_modules/**' 12 | - '!.gitignore' 13 | - '!.git/**' 14 | - '!.idea/**' 15 | 16 | custom: 17 | containers: 18 | scheduled-bash-job: 19 | directory: app 20 | events: 21 | - schedule: 22 | rate: '0 * * * *' 23 | -------------------------------------------------------------------------------- /containers/csharp-hello-world/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official .NET runtime image. 2 | FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base 3 | WORKDIR /app 4 | 5 | # Build stage 6 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build 7 | WORKDIR /src 8 | COPY *.csproj . 9 | RUN dotnet restore 10 | COPY . . 11 | RUN dotnet publish -c Release -o /app 12 | 13 | # Final stage 14 | FROM base AS final 15 | WORKDIR /app 16 | COPY --from=build /app . 17 | ENTRYPOINT ["dotnet", "HelloWorldApp.dll"] 18 | -------------------------------------------------------------------------------- /containers/csharp-hello-world/HelloWorldApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /containers/csharp-hello-world/Program.cs: -------------------------------------------------------------------------------- 1 | var builder = WebApplication.CreateBuilder(args); 2 | 3 | // Get PORT from environment, default to 8080 if not set 4 | var port = Environment.GetEnvironmentVariable("PORT") ?? "8080"; 5 | builder.WebHost.UseUrls($"http://*:{port}"); 6 | 7 | var app = builder.Build(); 8 | 9 | app.MapGet("/", () => "Hello from Scaleway!"); 10 | 11 | app.Run(); 12 | -------------------------------------------------------------------------------- /containers/function-handler-java/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # Package Files # 8 | *.jar 9 | 10 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 11 | hs_err_pid* 12 | 13 | # target folder 14 | */target 15 | -------------------------------------------------------------------------------- /containers/function-handler-java/README.md: -------------------------------------------------------------------------------- 1 | # Function handler written in Java 2 | 3 | A simple example to run Java Serverless Functions on top of Scaleway Serverless Containers 4 | 5 | The example is strongly inspired by this [Flask project written in Python](https://github.com/scaleway/serverless-scaleway-functions/tree/master/examples/container) with PORT definition by Environment variable. 6 | 7 | ## Setup 8 | 9 | - Install and configure [Serverless Framework](https://www.serverless.com/) 10 | - Install the [Scaleway plugin](https://github.com/scaleway/serverless-scaleway-functions) 11 | - For local testing install Java and Maven 12 | 13 | ### Build and Deploy 14 | 15 | The java-container folder contains a classic maven project with pom.xml and Dockerfile 16 | 17 | - Build maven project and test locally 18 | 19 | ``` 20 | mvn clean package 21 | ``` 22 | 23 | - Deploy your project to Scaleway Containers 24 | The Dockerfile build your maven project and create a new docker image. 25 | 26 | ``` 27 | serverless deploy 28 | ``` 29 | -------------------------------------------------------------------------------- /containers/function-handler-java/java-container/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Build stage 3 | # 4 | FROM maven:3.8.7-eclipse-temurin-17-alpine AS build 5 | COPY src /home/app/src 6 | COPY pom.xml /home/app 7 | RUN mvn -f /home/app/pom.xml clean package 8 | 9 | # 10 | # Package stage 11 | # 12 | FROM eclipse-temurin:17-jre-alpine 13 | COPY --from=build /home/app/target/serverless-scaleway-containers-1.0-jar-with-dependencies.jar /usr/local/lib/serverless-scaleway-containers.jar 14 | ENTRYPOINT ["java","-jar","/usr/local/lib/serverless-scaleway-containers.jar"] 15 | -------------------------------------------------------------------------------- /containers/function-handler-java/java-container/src/main/java/Handler.java: -------------------------------------------------------------------------------- 1 | import io.undertow.Undertow; 2 | import io.undertow.util.Headers; 3 | import org.apache.commons.lang3.SystemUtils; 4 | 5 | import static io.undertow.UndertowLogger.ROOT_LOGGER; 6 | 7 | public class Handler { 8 | 9 | public static final String DEFAULT_PORT = "8080"; 10 | 11 | public static void main(String[] args) { 12 | String port = SystemUtils.getEnvironmentVariable("PORT", DEFAULT_PORT); 13 | ROOT_LOGGER.infof("HTTP server listening on 0.0.0.0:" + port); 14 | Undertow server = Undertow.builder() 15 | .addHttpListener(Integer.parseInt(port), "0.0.0.0") 16 | .setHandler(exchange -> { 17 | exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json"); 18 | exchange.getResponseSender().send(""" 19 | { 20 | "message": "Hello, World from Scaleway Container !" 21 | }"""); 22 | }).build(); 23 | server.start(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /containers/function-handler-java/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "function-handler-java", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": {}, 12 | "devDependencies": { 13 | "serverless-scaleway-functions": "^0.4.5" 14 | }, 15 | "description": "" 16 | } 17 | -------------------------------------------------------------------------------- /containers/function-handler-java/serverless.yml: -------------------------------------------------------------------------------- 1 | service: scaleway-container 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | # Global Environment variables - used in every functions 6 | env: 7 | test: test 8 | 9 | plugins: 10 | - serverless-scaleway-functions 11 | 12 | package: 13 | patterns: 14 | - '!node_modules/**' 15 | - '!.gitignore' 16 | - '!.git/**' 17 | 18 | custom: 19 | containers: 20 | function-handler-java: 21 | directory: java-container 22 | # description: "" 23 | # minScale: 1 24 | # memoryLimit: 256 25 | # maxScale: 2 26 | # maxConcurrency: 50 27 | # timeout: 20000 28 | # port: 8080 29 | # httpOption: redirected 30 | # Local environment variables - used only in given function 31 | env: 32 | local: local 33 | -------------------------------------------------------------------------------- /containers/grpc-http2-go/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.22-bookworm as build 2 | 3 | # Set working directory 4 | WORKDIR /app 5 | 6 | # Copy required files 7 | COPY go.mod . 8 | COPY go.sum . 9 | COPY server/*.go ./server/ 10 | COPY hello.proto ./ 11 | 12 | # We install the protobuf compilation stack on the image directly to simplify workflow 13 | RUN apt-get update && apt-get install -y protobuf-compiler 14 | RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 15 | RUN go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 16 | 17 | # Generate go protobuf files 18 | RUN protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative hello.proto 19 | 20 | # Build the executable 21 | RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build server/main.go 22 | 23 | # Put the executable in a light scratch image 24 | FROM scratch 25 | 26 | COPY --from=build /app/main /server 27 | 28 | ENTRYPOINT ["/server"] -------------------------------------------------------------------------------- /containers/grpc-http2-go/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "log" 7 | "time" 8 | 9 | pb "github.com/scaleway/serverless-examples/containers/grpc-http2-go" 10 | "google.golang.org/grpc" 11 | ) 12 | 13 | const containerEndpoint = "YOUR_CONTAINER_ENDPOINT:80" 14 | 15 | func main() { 16 | flag.Parse() 17 | // Set up a connection to the server. 18 | conn, err := grpc.Dial(containerEndpoint, grpc.WithInsecure()) 19 | if err != nil { 20 | log.Fatalf("did not connect: %v", err) 21 | } 22 | 23 | defer conn.Close() 24 | c := pb.NewGreeterClient(conn) 25 | 26 | // Contact the server and print out its response. 27 | ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 28 | defer cancel() 29 | 30 | r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "Scaleway"}) 31 | if err != nil { 32 | log.Fatalf("could not greet: %v", err) 33 | } 34 | 35 | log.Printf("Greeting: %s", r.GetMessage()) 36 | } 37 | -------------------------------------------------------------------------------- /containers/grpc-http2-go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/scaleway/serverless-examples/containers/grpc-http2-go 2 | 3 | go 1.22.1 4 | 5 | require ( 6 | google.golang.org/grpc v1.63.2 7 | google.golang.org/protobuf v1.33.0 8 | ) 9 | 10 | require ( 11 | golang.org/x/net v0.24.0 // indirect 12 | golang.org/x/sys v0.19.0 // indirect 13 | golang.org/x/text v0.14.0 // indirect 14 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect 15 | ) 16 | 17 | replace github.com/scaleway/serverless-examples/containers/grpc-http2-go => . 18 | -------------------------------------------------------------------------------- /containers/grpc-http2-go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= 4 | golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= 5 | golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= 6 | golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 7 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 8 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 9 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= 10 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= 11 | google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= 12 | google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= 13 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 14 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 15 | -------------------------------------------------------------------------------- /containers/grpc-http2-go/hello.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "github.com/scaleway/serverless-examples/containers/grpc-http2-go"; 4 | 5 | package helloworld; 6 | 7 | // Greeting sample service 8 | service Greeter { 9 | rpc SayHello (HelloRequest) returns (HelloReply) {} 10 | } 11 | 12 | message HelloRequest { 13 | string name = 1; 14 | } 15 | 16 | message HelloReply { 17 | string message = 1; 18 | } 19 | -------------------------------------------------------------------------------- /containers/grpc-http2-go/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "net" 9 | 10 | pb "github.com/scaleway/serverless-examples/containers/grpc-http2-go" 11 | "google.golang.org/grpc" 12 | ) 13 | 14 | // Listening port, warning if you change it were, you must change it on your serverless container settings 15 | const defaultPort = 8080 16 | 17 | type server struct { 18 | pb.UnimplementedGreeterServer 19 | } 20 | 21 | // SayHello implements helloworld.GreeterServer 22 | func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { 23 | log.Printf("Received: %v", in.GetName()) 24 | return &pb.HelloReply{Message: "Hello " + in.GetName() + ". Sent by your container :)"}, nil 25 | } 26 | 27 | func main() { 28 | flag.Parse() 29 | 30 | lis, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", defaultPort)) 31 | if err != nil { 32 | log.Fatalf("failed to listen: %v", err) 33 | } 34 | 35 | s := grpc.NewServer() 36 | 37 | pb.RegisterGreeterServer(s, &server{}) 38 | 39 | log.Printf("server listening at %v", lis.Addr()) 40 | 41 | if err := s.Serve(lis); err != nil { 42 | log.Fatalf("failed to serve: %v", err) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /containers/nginx-cors-private-python/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated script 2 | curl.sh 3 | 4 | # Generated HTML 5 | index.html 6 | -------------------------------------------------------------------------------- /containers/nginx-cors-private-python/Makefile: -------------------------------------------------------------------------------- 1 | DOCKER_REGISTRY=rg.fr-par.scw.cloud 2 | DOCKER_REGISTRY_NS=cors-demo 3 | 4 | GATEWAY_VERSION=0.0.6 5 | SERVER_VERSION=0.0.1 6 | 7 | GATEWAY_TAG=$(DOCKER_REGISTRY)/$(DOCKER_REGISTRY_NS)/gateway:$(GATEWAY_VERSION) 8 | SERVER_TAG=$(DOCKER_REGISTRY)/$(DOCKER_REGISTRY_NS)/server:$(SERVER_VERSION) 9 | 10 | .PHONY: create-namespace 11 | create-namespace: 12 | scw registry namespace create name=$(DOCKER_REGISTRY_NS) 13 | 14 | .PHONY: docker-login 15 | docker-login: 16 | scw registry login 17 | 18 | .PHONY: build-server 19 | build-server: 20 | cd server && docker build -t $(SERVER_TAG) . 21 | 22 | .PHONY: build-gateway 23 | build-gateway: 24 | cd gateway && docker build -t $(GATEWAY_TAG) . 25 | 26 | .PHONY: push-server 27 | push-server: 28 | docker push $(SERVER_TAG) 29 | 30 | .PHONY: push-gateway 31 | push-gateway: 32 | docker push $(GATEWAY_TAG) 33 | 34 | .PHONY: tf-init 35 | tf-init: 36 | cd terraform && terraform init 37 | 38 | .PHONY: tf-plan 39 | tf-plan: 40 | cd terraform && terraform plan -var-file=vars/main.tfvars 41 | 42 | .PHONY: tf-apply 43 | tf-apply: 44 | cd terraform && terraform apply -auto-approve -var-file=vars/main.tfvars 45 | -------------------------------------------------------------------------------- /containers/nginx-cors-private-python/curl.tftpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # URLs of gateway and server functions 6 | GATEWAY_URL=https://${ gateway_func_url } 7 | SERVER_URL=https://${ server_func_url } 8 | 9 | # Auth token 10 | TOKEN=${ server_auth_token } 11 | 12 | echo "--- Invoking private endpoint directly ---" 13 | echo "URL = $SERVER_URL" 14 | 15 | # Do the curl 16 | echo "" 17 | echo "GET" 18 | curl -H "X-Auth-Token: $TOKEN" $SERVER_URL 19 | echo "" 20 | 21 | echo "" 22 | echo "--- Invoking private endpoint through gateway ---" 23 | echo "URL = $GATEWAY_URL" 24 | 25 | # Do the curl 26 | echo "" 27 | echo "OPTIONS" 28 | curl -i -X OPTIONS -H "X-Auth-Token: $TOKEN" $GATEWAY_URL 29 | 30 | echo "GET" 31 | curl -i -H "X-Auth-Token: $TOKEN" $GATEWAY_URL 32 | echo "" 33 | -------------------------------------------------------------------------------- /containers/nginx-cors-private-python/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | gateway: 5 | build: 6 | context: ./gateway 7 | ports: 8 | - "8080:8080" 9 | depends_on: 10 | - server 11 | environment: 12 | SERVER_CONTAINER_URL: "server:8080" 13 | 14 | server: 15 | build: 16 | context: ./server 17 | image: rg.fr-par.scw.cloud/cors-demo/server:0.0.1 18 | ports: 19 | - "8081:8080" 20 | -------------------------------------------------------------------------------- /containers/nginx-cors-private-python/gateway/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.23 2 | 3 | # See env var subs: https://hub.docker.com/_/nginx/ 4 | COPY conf.template /etc/nginx/templates/default.conf.template 5 | -------------------------------------------------------------------------------- /containers/nginx-cors-private-python/gateway/conf.template: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8080; 3 | server_name gateway; 4 | error_log /var/log/gateway-error.log warn; 5 | access_log /var/log/gateway-access.log; 6 | 7 | client_body_buffer_size 32k; 8 | client_header_buffer_size 8k; 9 | large_client_header_buffers 4 32k; 10 | 11 | location / { 12 | if ($request_method = OPTIONS) { 13 | add_header 'Access-Control-Allow-Origin' '*'; 14 | add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH'; 15 | add_header 'Access-Control-Allow-Credentials' 'true'; 16 | add_header 'Access-Control-Allow-Headers' '*'; 17 | 18 | add_header 'Content-Type' 'text/plain charset=UTF-8'; 19 | add_header 'Content-Length' 0; 20 | 21 | add_header 'Server' 'nginx'; 22 | 23 | return 200; 24 | } 25 | 26 | proxy_redirect off; 27 | proxy_http_version 1.1; 28 | proxy_pass_request_headers on; 29 | 30 | proxy_set_header X-Real-IP $remote_addr; 31 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 32 | 33 | proxy_pass http://${SERVER_CONTAINER_URL}; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /containers/nginx-cors-private-python/server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3-alpine 2 | WORKDIR /usr/src/app 3 | 4 | COPY requirements.txt . 5 | RUN pip install -qr requirements.txt 6 | COPY server.py . 7 | 8 | WORKDIR /images 9 | COPY hello.png . 10 | COPY dummy.png . 11 | 12 | WORKDIR /usr/src/app 13 | CMD ["python3", "./server.py"] 14 | -------------------------------------------------------------------------------- /containers/nginx-cors-private-python/server/dummy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/containers/nginx-cors-private-python/server/dummy.png -------------------------------------------------------------------------------- /containers/nginx-cors-private-python/server/hello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/containers/nginx-cors-private-python/server/hello.png -------------------------------------------------------------------------------- /containers/nginx-cors-private-python/server/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | -------------------------------------------------------------------------------- /containers/nginx-cors-private-python/terraform/.gitignore: -------------------------------------------------------------------------------- 1 | state 2 | state.backup 3 | .terraform/ 4 | .terraform.lock.hcl 5 | -------------------------------------------------------------------------------- /containers/nginx-cors-private-python/terraform/vars/main.tfvars: -------------------------------------------------------------------------------- 1 | project_id = "your-project-id" 2 | -------------------------------------------------------------------------------- /containers/nginx-hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /containers/nginx-hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nginx-hello-world", 3 | "version": "1.0.0", 4 | "keywords": [], 5 | "author": "", 6 | "license": "ISC", 7 | "dependencies": {}, 8 | "devDependencies": { 9 | "serverless-scaleway-functions": ">=0.4.8" 10 | }, 11 | "description": "" 12 | } 13 | -------------------------------------------------------------------------------- /containers/nginx-hello-world/serverless.yml: -------------------------------------------------------------------------------- 1 | service: nginx-hello 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | 6 | plugins: 7 | - serverless-scaleway-functions 8 | 9 | package: 10 | patterns: 11 | - "!node_modules/**" 12 | - "!.gitignore" 13 | - "!.git/**" 14 | 15 | custom: 16 | containers: 17 | first: 18 | registryImage: nginx 19 | port: 80 20 | memoryLimit: 256 21 | cpuLimit: 140 22 | -------------------------------------------------------------------------------- /containers/python-hello-world/README.md: -------------------------------------------------------------------------------- 1 | # Python Hello World 2 | 3 | This demonstrates a simple example of running a [Flask](https://flask.palletsprojects.com/en/2.0.x/quickstart/) HTTP server in a [Scaleway Serverless Container](https://www.scaleway.com/en/serverless-containers/). 4 | 5 | ## Requirements 6 | 7 | This example assumes you are familiar with how Serverless Containers work. If needed, you can 8 | check the [Scaleway official documentation](https://www.scaleway.com/en/docs/serverless/containers/quickstart/). 9 | 10 | This example uses the Scaleway Serverless Framework Plugin. Please set up your environment with the requirements stated in the [Scaleway Serverless Framework Plugin](https://github.com/scaleway/serverless-scaleway-functions) before trying out the example. 11 | 12 | ## Deployment 13 | 14 | Once your environment is set up, you can deploy your container with: 15 | 16 | ```sh 17 | npm i 18 | 19 | serverless deploy 20 | ``` 21 | 22 | When the deployment is complete, you should be able to `curl` the container's endpoint or hit it from a browser and see the message returned by the Flask server. 23 | -------------------------------------------------------------------------------- /containers/python-hello-world/container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10 2 | WORKDIR /app 3 | 4 | RUN pip3 install --upgrade pip 5 | COPY requirements.txt . 6 | RUN pip3 install -r requirements.txt --target . 7 | 8 | COPY handler.py . 9 | 10 | CMD [ "python3", "./handler.py" ] 11 | -------------------------------------------------------------------------------- /containers/python-hello-world/container/handler.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | import os 3 | 4 | app = Flask(__name__) 5 | 6 | 7 | @app.route("/") 8 | def hello(): 9 | return "Hello world from the Python container!" 10 | 11 | 12 | if __name__ == "__main__": 13 | port_env = os.getenv("PORT", 8080) 14 | port = int(port_env) 15 | app.run(debug=True, host="0.0.0.0", port=port) 16 | -------------------------------------------------------------------------------- /containers/python-hello-world/container/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==3.0.0 2 | -------------------------------------------------------------------------------- /containers/python-hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello-python-container", 3 | "version": "1.0.0", 4 | "keywords": [], 5 | "author": "", 6 | "license": "ISC", 7 | "dependencies": {}, 8 | "devDependencies": { 9 | "serverless-scaleway-functions": ">=0.4.8" 10 | }, 11 | "description": "" 12 | } 13 | -------------------------------------------------------------------------------- /containers/python-hello-world/serverless.yml: -------------------------------------------------------------------------------- 1 | service: python-hello 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | 6 | plugins: 7 | - serverless-scaleway-functions 8 | 9 | package: 10 | patterns: 11 | - "!node_modules/**" 12 | - "!.gitignore" 13 | - "!.git/**" 14 | 15 | custom: 16 | containers: 17 | hello-py: 18 | directory: container 19 | port: 8080 20 | memoryLimit: 256 21 | cpuLimit: 140 22 | -------------------------------------------------------------------------------- /containers/python-s3-upload/container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10 2 | WORKDIR /app 3 | 4 | RUN pip3 install --upgrade pip 5 | COPY requirements.txt . 6 | RUN pip3 install -r requirements.txt --target . 7 | 8 | COPY app.py . 9 | 10 | CMD [ "python3", "./app.py" ] 11 | -------------------------------------------------------------------------------- /containers/python-s3-upload/container/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3==1.34.30 2 | chardet==4.0.0 3 | Flask==2.2.2 4 | -------------------------------------------------------------------------------- /containers/python-s3-upload/terraform/container.tf: -------------------------------------------------------------------------------- 1 | resource scaleway_container_namespace main { 2 | name = "python-s3-example" 3 | } 4 | 5 | resource scaleway_container main { 6 | name = "python-s3-example" 7 | description = "S3 file uploader" 8 | namespace_id = scaleway_container_namespace.main.id 9 | registry_image = docker_image.main.name 10 | port = 8080 11 | cpu_limit = 1000 12 | memory_limit = 1024 13 | min_scale = 0 14 | max_scale = 1 15 | privacy = "public" 16 | deploy = true 17 | environment_variables = { 18 | "BUCKET_NAME" = scaleway_object_bucket.main.name 19 | } 20 | secret_environment_variables = { 21 | "ACCESS_KEY" = var.access_key 22 | "SECRET_KEY" = var.secret_key 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /containers/python-s3-upload/terraform/image.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_registry_namespace" "main" { 2 | name = "s3-example-${random_string.suffix.result}" 3 | region = "fr-par" 4 | project_id = var.project_id 5 | } 6 | 7 | resource "docker_image" "main" { 8 | name = "${scaleway_registry_namespace.main.endpoint}/s3-example:${var.image_version}" 9 | build { 10 | context = "${path.cwd}/../container" 11 | } 12 | 13 | provisioner "local-exec" { 14 | command = "docker push ${docker_image.main.name}" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /containers/python-s3-upload/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "bucket_name" { 2 | value = scaleway_object_bucket.main.name 3 | } 4 | 5 | output "endpoint" { 6 | value = scaleway_container.main.domain_name 7 | } 8 | -------------------------------------------------------------------------------- /containers/python-s3-upload/terraform/providers.tf: -------------------------------------------------------------------------------- 1 | provider "scaleway" { 2 | zone = "fr-par-1" 3 | region = "fr-par" 4 | access_key = var.access_key 5 | secret_key = var.secret_key 6 | project_id = var.project_id 7 | } 8 | 9 | provider "docker" { 10 | host = "unix:///var/run/docker.sock" 11 | 12 | registry_auth { 13 | address = scaleway_registry_namespace.main.endpoint 14 | username = "nologin" 15 | password = var.secret_key 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /containers/python-s3-upload/terraform/s3.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_object_bucket" "main" { 2 | name = "python-s3-example-${random_string.suffix.result}" 3 | } 4 | -------------------------------------------------------------------------------- /containers/python-s3-upload/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "access_key" { 2 | type = string 3 | } 4 | 5 | variable "secret_key" { 6 | type = string 7 | } 8 | 9 | variable "project_id" { 10 | type = string 11 | } 12 | 13 | variable "image_version" { 14 | type = string 15 | default = "0.0.2" 16 | } 17 | 18 | resource "random_string" "suffix" { 19 | length = 8 20 | upper = false 21 | special = false 22 | } 23 | -------------------------------------------------------------------------------- /containers/python-s3-upload/terraform/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | scaleway = { 4 | source = "scaleway/scaleway" 5 | } 6 | docker = { 7 | source = "kreuzwerker/docker" 8 | version = "3.0.2" 9 | } 10 | } 11 | required_version = ">= 0.13" 12 | } 13 | -------------------------------------------------------------------------------- /containers/ruby-hello-world/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official Ruby image from the Docker Hub 2 | FROM ruby:3.4.4 3 | 4 | # Set the working directory in the container 5 | WORKDIR /app 6 | 7 | # Copy the Gemfile and Gemfile.lock into the container 8 | COPY Gemfile ./ 9 | 10 | # Install the dependencies 11 | RUN bundle install && bundle add rackup puma 12 | 13 | # Copy the rest of the application code into the container 14 | COPY app.rb . 15 | 16 | # Run the application 17 | CMD ["ruby", "app.rb"] 18 | -------------------------------------------------------------------------------- /containers/ruby-hello-world/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'webrick' 4 | -------------------------------------------------------------------------------- /containers/ruby-hello-world/app.rb: -------------------------------------------------------------------------------- 1 | require 'webrick' 2 | 3 | # Read the PORT environment variable, default to 8000 if not set 4 | port = ENV['PORT'] || 8000 5 | 6 | server = WEBrick::HTTPServer.new(Port: port.to_i) 7 | 8 | server.mount_proc '/' do |req, res| 9 | res.body = "Hello from Scaleway!" 10 | end 11 | 12 | trap('INT') { server.shutdown } 13 | server.start 14 | -------------------------------------------------------------------------------- /containers/rust-hello-world/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello-world" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /containers/rust-hello-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /containers/rust-hello-world/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official Rust image as the build stage 2 | FROM rust:1.87.0-alpine3.21 as builder 3 | 4 | # Set the working directory inside the container 5 | WORKDIR /usr/src/app 6 | 7 | # Copy the source code to the working directory 8 | COPY . . 9 | 10 | # Build the Rust application 11 | RUN cargo build --release 12 | 13 | # Use a smaller base image for the final container 14 | FROM alpine:3.21 15 | 16 | # Copy the compiled binary from the build stage 17 | COPY --from=builder /usr/src/app/target/release/hello-world /usr/local/bin/hello-world 18 | 19 | # Set the entrypoint command 20 | CMD ["hello-world"] 21 | -------------------------------------------------------------------------------- /containers/rust-hello-world/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env, 3 | io::{BufReader, prelude::*}, 4 | net::{TcpListener, TcpStream}, 5 | }; 6 | 7 | fn main() { 8 | let port = env::var("PORT").unwrap_or_else(|_| "8080".to_string()); 9 | let listener = TcpListener::bind(format!("0.0.0.0:{}", port)).unwrap(); 10 | 11 | for stream in listener.incoming() { 12 | let stream = stream.unwrap(); 13 | 14 | handle_connection(stream); 15 | } 16 | } 17 | 18 | fn handle_connection(mut stream: TcpStream) { 19 | let buf_reader = BufReader::new(&mut stream); 20 | 21 | // variable not used but you can manipulate it to get useful informations of the incoming request and manage parameters. 22 | let _http_request: Vec<_> = buf_reader 23 | .lines() 24 | .map(|result| result.unwrap()) 25 | .take_while(|line| !line.is_empty()) 26 | .collect(); 27 | 28 | let status_line = "HTTP/1.1 200 OK"; 29 | let contents = "Hello from Scaleway!"; 30 | let length = contents.len(); 31 | 32 | let response = format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"); 33 | 34 | stream.write_all(response.as_bytes()).unwrap(); 35 | } 36 | -------------------------------------------------------------------------------- /containers/terraform-nginx-hello-world/README.md: -------------------------------------------------------------------------------- 1 | # Terraform NGINX hello world 2 | 3 | This demonstrates a simple example of deploying a container with Terraform on [Scaleway Serverless Containers](https://www.scaleway.com/en/serverless-containers/). 4 | 5 | ## Requirements 6 | 7 | This example assumes you are familiar with how serverless containers work. If needed, you can 8 | check the [Scaleway official documentation](https://www.scaleway.com/en/docs/serverless/containers/quickstart/). 9 | 10 | This example uses Terraform. Please install and configure Terraform before trying out the example. 11 | 12 | ## Setup 13 | 14 | Set up your scaleway credentials with: 15 | ```sh 16 | export TF_VAR_access_key= 17 | export TF_VAR_secret_key= 18 | export TF_VAR_project_id= 19 | ``` 20 | 21 | Set up Terraform with: 22 | ```sh 23 | terraform init 24 | ``` 25 | 26 | ## Deployment 27 | 28 | Once your environment is set up, you can: 29 | 30 | Run plan, have a look at what will be created: 31 | ```sh 32 | terraform plan 33 | ``` 34 | 35 | Deploy with: 36 | ```sh 37 | terraform apply 38 | ``` 39 | 40 | When the deployment is complete, you can check the deployment succeeded either by: 41 | 42 | i) curl the container's endpoint with: 43 | ```sh 44 | curl $(terraform output -raw endpoint) 45 | ``` 46 | ii) hit it from a browser and see the NGINX default page. 47 | -------------------------------------------------------------------------------- /containers/terraform-nginx-hello-world/main.tf: -------------------------------------------------------------------------------- 1 | resource scaleway_container_namespace main { 2 | name = "serverless-example-ns" 3 | description = "Namespace managed by terraform" 4 | } 5 | 6 | resource scaleway_container main { 7 | name = "serverless-example-container" 8 | description = "NGINX container deployed with terraform" 9 | namespace_id = scaleway_container_namespace.main.id 10 | registry_image = "docker.io/library/nginx:latest" 11 | port = 80 12 | cpu_limit = 1120 13 | memory_limit = 1024 14 | min_scale = 0 15 | max_scale = 1 16 | privacy = "public" 17 | protocol = "http1" 18 | deploy = true 19 | } 20 | -------------------------------------------------------------------------------- /containers/terraform-nginx-hello-world/outputs.tf: -------------------------------------------------------------------------------- 1 | output "endpoint" { 2 | value = scaleway_container.main.domain_name 3 | } 4 | -------------------------------------------------------------------------------- /containers/terraform-nginx-hello-world/providers.tf: -------------------------------------------------------------------------------- 1 | provider "scaleway" { 2 | zone = "fr-par-1" 3 | region = "fr-par" 4 | access_key = var.access_key 5 | secret_key = var.secret_key 6 | project_id = var.project_id 7 | } 8 | -------------------------------------------------------------------------------- /containers/terraform-nginx-hello-world/variables.tf: -------------------------------------------------------------------------------- 1 | variable "access_key" { 2 | type = string 3 | } 4 | 5 | variable "secret_key" { 6 | type = string 7 | } 8 | 9 | variable "project_id" { 10 | type = string 11 | } 12 | -------------------------------------------------------------------------------- /containers/terraform-nginx-hello-world/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | scaleway = { 4 | source = "scaleway/scaleway" 5 | } 6 | } 7 | required_version = ">= 0.13" 8 | } 9 | -------------------------------------------------------------------------------- /containers/terraform-triggers/.gitignore: -------------------------------------------------------------------------------- 1 | secrets.auto.tfvars 2 | -------------------------------------------------------------------------------- /containers/terraform-triggers/docker.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | docker_image_tag = sha256(join("", [for f in fileset(path.module, "docker/${var.container_language}/*") : filesha256(f)])) 3 | } 4 | 5 | resource "docker_image" "main" { 6 | name = "${scaleway_container_namespace.main.registry_endpoint}/server-${var.container_language}:${local.docker_image_tag}" 7 | build { 8 | context = "docker/${var.container_language}" 9 | platform = "amd64" 10 | } 11 | triggers = { 12 | tag = local.docker_image_tag 13 | } 14 | } 15 | 16 | resource "docker_registry_image" "main" { 17 | name = docker_image.main.name 18 | keep_remotely = true # keep old images 19 | } 20 | -------------------------------------------------------------------------------- /containers/terraform-triggers/docker/go/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.21-alpine AS builder 2 | WORKDIR /app 3 | 4 | COPY . ./ 5 | RUN go build -o server 6 | 7 | FROM alpine 8 | 9 | COPY --from=builder /app/server /app/server 10 | 11 | CMD ["/app/server"] 12 | -------------------------------------------------------------------------------- /containers/terraform-triggers/docker/go/go.mod: -------------------------------------------------------------------------------- 1 | module example 2 | 3 | go 1.21 4 | -------------------------------------------------------------------------------- /containers/terraform-triggers/docker/go/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net/http" 8 | ) 9 | 10 | func handle(w http.ResponseWriter, r *http.Request) { 11 | body, err := io.ReadAll(r.Body) 12 | if err != nil { 13 | w.WriteHeader(http.StatusInternalServerError) 14 | return 15 | } 16 | 17 | log.Println(string(body)) 18 | fmt.Fprintln(w, "Hello from container") 19 | } 20 | 21 | func main() { 22 | http.HandleFunc("/", handle) 23 | 24 | log.Println("Starting!") 25 | log.Fatal(http.ListenAndServe("0.0.0.0:8080", nil)) 26 | } 27 | -------------------------------------------------------------------------------- /containers/terraform-triggers/docker/python/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3-alpine 2 | WORKDIR /usr/src/app 3 | 4 | COPY requirements.txt . 5 | RUN pip install -qr requirements.txt 6 | COPY server.py . 7 | 8 | WORKDIR /usr/src/app 9 | CMD ["python3", "./server.py"] 10 | -------------------------------------------------------------------------------- /containers/terraform-triggers/docker/python/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask~=3.0.0 2 | -------------------------------------------------------------------------------- /containers/terraform-triggers/docker/python/server.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, make_response, request 2 | 3 | app = Flask(__name__) 4 | 5 | HTTP_METHODS = ["GET", "POST"] 6 | 7 | 8 | @app.route("/", methods=HTTP_METHODS) 9 | def root(): 10 | print(request.get_data(), flush=True) 11 | response = make_response("Hello from container") 12 | return response 13 | 14 | 15 | if __name__ == "__main__": 16 | app.run(debug=True, host="0.0.0.0", port="8080") 17 | -------------------------------------------------------------------------------- /containers/terraform-triggers/mnq_nats.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_mnq_nats_account" "main" { 2 | name = "nats-account" 3 | } 4 | 5 | resource "scaleway_mnq_nats_credentials" "main" { 6 | account_id = scaleway_mnq_nats_account.main.id 7 | name = "triggers-nats" 8 | } 9 | 10 | resource "local_sensitive_file" "nats" { 11 | filename = "nats-creds" 12 | content = "${scaleway_mnq_nats_credentials.main.file}" 13 | } 14 | -------------------------------------------------------------------------------- /containers/terraform-triggers/mnq_sqs.tf: -------------------------------------------------------------------------------- 1 | # Admin credentials used to create the queues, and to send messages (see tests/send_messages.py) 2 | resource "scaleway_mnq_sqs_credentials" "main" { 3 | permissions { 4 | can_publish = true 5 | can_receive = true 6 | can_manage = true 7 | } 8 | } 9 | 10 | locals { 11 | sqs_admin_credentials_access_key = scaleway_mnq_sqs_credentials.main.access_key 12 | sqs_admin_credentials_secret_key = scaleway_mnq_sqs_credentials.main.secret_key 13 | } 14 | 15 | resource "scaleway_mnq_sqs_queue" "public" { 16 | name = "sqs-queue-public" 17 | 18 | access_key = local.sqs_admin_credentials_access_key 19 | secret_key = local.sqs_admin_credentials_secret_key 20 | } 21 | 22 | resource "scaleway_mnq_sqs_queue" "private" { 23 | name = "sqs-queue-private" 24 | 25 | access_key = local.sqs_admin_credentials_access_key 26 | secret_key = local.sqs_admin_credentials_secret_key 27 | } 28 | -------------------------------------------------------------------------------- /containers/terraform-triggers/provider.tf: -------------------------------------------------------------------------------- 1 | provider "scaleway" { 2 | zone = "fr-par-1" 3 | region = "fr-par" 4 | access_key = var.access_key 5 | secret_key = var.secret_key 6 | project_id = var.project_id 7 | } 8 | 9 | provider "docker" { 10 | registry_auth { 11 | address = var.scw_registry 12 | username = var.access_key 13 | password = var.secret_key 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /containers/terraform-triggers/tests/.gitignore: -------------------------------------------------------------------------------- 1 | env/ 2 | -------------------------------------------------------------------------------- /containers/terraform-triggers/tests/requirements.txt: -------------------------------------------------------------------------------- 1 | # Scaleway M&Q product does not support AWS JSON protocol for now (https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-json-faqs.html), so we must use boto versions less than 1.xx.81 2 | boto3<1.28.81 3 | botocore<1.31.81 4 | nats-py==2.6.0 5 | nkeys==0.1.0 6 | -------------------------------------------------------------------------------- /containers/terraform-triggers/triggers.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_container_trigger" "public_sqs" { 2 | container_id = scaleway_container.public.id 3 | name = "public-sqs-trigger" 4 | sqs { 5 | queue = scaleway_mnq_sqs_queue.public.name 6 | } 7 | } 8 | 9 | resource "scaleway_container_trigger" "private_sqs" { 10 | container_id = scaleway_container.private.id 11 | name = "private-sqs-trigger" 12 | sqs { 13 | queue = scaleway_mnq_sqs_queue.private.name 14 | } 15 | } 16 | 17 | locals { 18 | public_nats_subject = "public-nats-subject" 19 | private_nats_subject = "private-nats-subject" 20 | } 21 | 22 | resource "scaleway_container_trigger" "public_nats" { 23 | container_id = scaleway_container.public.id 24 | name = "public-nats-trigger" 25 | nats { 26 | account_id = scaleway_mnq_nats_account.main.id 27 | subject = local.public_nats_subject 28 | } 29 | } 30 | 31 | resource "scaleway_container_trigger" "private_nats" { 32 | container_id = scaleway_container.private.id 33 | name = "private-nats-trigger" 34 | nats { 35 | account_id = scaleway_mnq_nats_account.main.id 36 | subject = local.private_nats_subject 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /containers/terraform-triggers/variables.tf: -------------------------------------------------------------------------------- 1 | variable "access_key" { 2 | type = string 3 | } 4 | 5 | variable "secret_key" { 6 | type = string 7 | } 8 | 9 | variable "project_id" { 10 | type = string 11 | } 12 | 13 | variable "scw_registry" { 14 | type = string 15 | default = "rg.fr-par.scw.cloud" 16 | } 17 | 18 | variable "container_language" { 19 | type = string 20 | default = "python" 21 | } 22 | -------------------------------------------------------------------------------- /containers/terraform-triggers/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | scaleway = { 4 | source = "scaleway/scaleway" 5 | version = ">=2.31.0" 6 | } 7 | docker = { 8 | source = "kreuzwerker/docker" 9 | version = "3.0.2" 10 | } 11 | time = { 12 | source = "hashicorp/time" 13 | version = "0.9.1" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /functions/badge-php/.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.lock 3 | node_modules 4 | .serverless 5 | package-lock.json 6 | -------------------------------------------------------------------------------- /functions/badge-php/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "twig/twig": "^3.5" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /functions/badge-php/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "respository-badge-php", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": {}, 12 | "devDependencies": { 13 | "serverless-scaleway-functions": "^0.4.5" 14 | }, 15 | "description": "" 16 | } 17 | -------------------------------------------------------------------------------- /functions/badge-php/serverless.yml: -------------------------------------------------------------------------------- 1 | service: repository-badge-php 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | runtime: php82 6 | 7 | plugins: 8 | - serverless-scaleway-functions 9 | 10 | package: 11 | patterns: 12 | - '!node_modules/**' 13 | - "!..gitignore" 14 | - "!.git/**" 15 | 16 | functions: 17 | repository-badge: 18 | handler: handler.handler 19 | -------------------------------------------------------------------------------- /functions/cors-go/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .serverless/ 4 | -------------------------------------------------------------------------------- /functions/cors-go/go.mod: -------------------------------------------------------------------------------- 1 | module cors-go 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/scaleway/serverless-functions-go v0.1.2 7 | github.com/stretchr/testify v1.8.2 8 | ) 9 | 10 | require ( 11 | github.com/davecgh/go-spew v1.1.1 // indirect 12 | github.com/google/uuid v1.3.0 // indirect 13 | github.com/kr/text v0.2.0 // indirect 14 | github.com/pmezard/go-difflib v1.0.0 // indirect 15 | github.com/rogpeppe/go-internal v1.11.0 // indirect 16 | gopkg.in/yaml.v3 v3.0.1 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /functions/cors-go/handler.go: -------------------------------------------------------------------------------- 1 | package cors 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "net/http/httputil" 8 | ) 9 | 10 | // See: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#the_http_response_headers 11 | func setCorsHeaders(w http.ResponseWriter) { 12 | w.Header().Set("Access-Control-Allow-Headers", "*") 13 | w.Header().Set("Access-Control-Allow-Methods", "*") 14 | w.Header().Set("Access-Control-Allow-Origin", "*") 15 | } 16 | 17 | func HandleWithCors(w http.ResponseWriter, r *http.Request) { 18 | dump, err := httputil.DumpRequest(r, true) 19 | if err != nil { 20 | http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) 21 | return 22 | } 23 | fmt.Printf("%q", dump) 24 | 25 | // Sets the response headers to allow CORS requests. 26 | setCorsHeaders(w) 27 | 28 | w.Header().Set("Content-type", "text/plain") 29 | w.WriteHeader(http.StatusOK) 30 | 31 | _, err = io.WriteString(w, "This function is allowing most CORS requests") 32 | if err != nil { 33 | http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /functions/cors-go/handler_test.go: -------------------------------------------------------------------------------- 1 | package cors 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | const offlineTestingServer = "http://localhost:8080" 12 | 13 | func TestHandleWithCors(t *testing.T) { 14 | 15 | resp, err := http.Get(offlineTestingServer) 16 | assert.NoError(t, err) 17 | 18 | defer resp.Body.Close() 19 | 20 | assert.Equal(t, http.StatusOK, resp.StatusCode) 21 | assert.Equal(t, "*", resp.Header.Get("Access-Control-Allow-Headers")) 22 | assert.Equal(t, "*", resp.Header.Get("Access-Control-Allow-Methods")) 23 | assert.Equal(t, "*", resp.Header.Get("Access-Control-Allow-Origin")) 24 | assert.Equal(t, "text/plain", resp.Header.Get("Content-Type")) 25 | 26 | bodyBytes, err := io.ReadAll(resp.Body) 27 | assert.NoError(t, err) 28 | assert.Equal(t, "This function is allowing most CORS requests", string(bodyBytes)) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /functions/cors-go/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cors-go", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": {}, 12 | "devDependencies": { 13 | "serverless-scaleway-functions": "^0.4.7" 14 | }, 15 | "description": "" 16 | } 17 | -------------------------------------------------------------------------------- /functions/cors-go/serverless.yml: -------------------------------------------------------------------------------- 1 | service: cors-go 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | runtime: go122 6 | 7 | plugins: 8 | - serverless-scaleway-functions 9 | 10 | package: 11 | patterns: 12 | - "!node_modules/**" 13 | - "!.gitignore" 14 | - "!.git/**" 15 | 16 | functions: 17 | cors-permissive: 18 | handler: "HandleWithCors" 19 | -------------------------------------------------------------------------------- /functions/cors-go/test/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | handler "cors-go" 5 | 6 | "github.com/scaleway/serverless-functions-go/local" 7 | ) 8 | 9 | func main() { 10 | // Replace "Handle" with your function handler name if necessary 11 | local.ServeHandler(handler.HandleWithCors, local.WithPort(8080)) 12 | } 13 | -------------------------------------------------------------------------------- /functions/cors-node/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .serverless/ 4 | -------------------------------------------------------------------------------- /functions/cors-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cors-node", 3 | "version": "1.0.0", 4 | "main": "handler.js", 5 | "type": "module", 6 | "keywords": [], 7 | "author": "", 8 | "license": "ISC", 9 | "dependencies": {}, 10 | "devDependencies": { 11 | "@scaleway/serverless-functions": "^1.0.1", 12 | "serverless-scaleway-functions": "^0.4.4" 13 | }, 14 | "description": "" 15 | } 16 | -------------------------------------------------------------------------------- /functions/cors-node/serverless.yml: -------------------------------------------------------------------------------- 1 | service: cors-node 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | runtime: node18 6 | 7 | plugins: 8 | - serverless-scaleway-functions 9 | 10 | package: 11 | patterns: 12 | - "!node_modules/**" 13 | - "!.gitignore" 14 | - "!.git/**" 15 | 16 | functions: 17 | cors-permissive: 18 | handler: handler.handleCorsPermissive 19 | cors-very-permissive: 20 | handler: handler.handleCorsVeryPermissive 21 | -------------------------------------------------------------------------------- /functions/cors-python/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .serverless/ 4 | -------------------------------------------------------------------------------- /functions/cors-python/handler.py: -------------------------------------------------------------------------------- 1 | def handle(event, context): 2 | 3 | print(event) 4 | print(context) 5 | 6 | return { 7 | "body": "This is checking CORS", 8 | "headers": { 9 | "Access-Control-Allow-Origin": "*", 10 | "Access-Control-Allow-Headers": "*", 11 | "Access-Control-Allow-Methods": "*", 12 | "Content-Type": ["text/plain"], 13 | }, 14 | } 15 | 16 | if __name__ == "__main__": 17 | from scaleway_functions_python import local 18 | local.serve_handler(handle) 19 | -------------------------------------------------------------------------------- /functions/cors-python/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cors-python", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": {}, 12 | "devDependencies": { 13 | "serverless-scaleway-functions": "^0.4.4" 14 | }, 15 | "description": "" 16 | } 17 | -------------------------------------------------------------------------------- /functions/cors-python/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | scaleway_functions_python==0.2.0 2 | requests==2.28.2 3 | pytest==7.2.2 -------------------------------------------------------------------------------- /functions/cors-python/serverless.yml: -------------------------------------------------------------------------------- 1 | service: cors-python 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | runtime: python310 6 | env: 7 | test: test 8 | 9 | plugins: 10 | - serverless-scaleway-functions 11 | 12 | package: 13 | patterns: 14 | - '!node_modules/**' 15 | - '!.gitignore' 16 | - '!.git/**' 17 | 18 | functions: 19 | first: 20 | handler: handler.handle 21 | -------------------------------------------------------------------------------- /functions/cors-python/test/cors_python_test.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | OFFLINE_TESTING_SERVER="http://localhost:8080" 4 | 5 | def test_handler_offline(): 6 | response = requests.get(OFFLINE_TESTING_SERVER) 7 | assert response.headers["Access-Control-Allow-Origin"] == "*" 8 | assert response.headers["Access-Control-Allow-Headers"] == "*" 9 | assert response.headers["Access-Control-Allow-Methods"] == "*" 10 | assert response.headers["Content-Type"] == "text/plain" 11 | assert response.text == "This is checking CORS" 12 | -------------------------------------------------------------------------------- /functions/cors-rust/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | node_modules/ 3 | package-lock.json 4 | .serverless/ 5 | target/ 6 | -------------------------------------------------------------------------------- /functions/cors-rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cors-rust" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | axum = "0.8.1" 10 | http = "1.3.1" -------------------------------------------------------------------------------- /functions/cors-rust/README.md: -------------------------------------------------------------------------------- 1 | # Rust CORS example 2 | 3 | This function shows how to handle preflight CORS requests that will be sent by a browser when invoking a function. 4 | 5 | The example uses totally permissive, open CORS, you will may want to modify this to make it more secure. 6 | 7 | The second handler is even more permissive, allowing authentification tokens such as cookies to be transmitted to the function. 8 | 9 | ## Setup 10 | 11 | This examples uses the [Scaleway Serverless Framework Plugin](https://github.com/scaleway/serverless-scaleway-functions). 12 | 13 | Once this is set up, you can run: 14 | 15 | ```console 16 | npm install 17 | 18 | serverless deploy 19 | ``` 20 | 21 | Then, from the given URL, you can run: 22 | 23 | ```console 24 | # Options request 25 | curl -i -X OPTIONS 26 | 27 | # Get request 28 | curl -i -X GET 29 | ``` 30 | -------------------------------------------------------------------------------- /functions/cors-rust/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cors-rust", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": {}, 12 | "devDependencies": { 13 | "serverless-scaleway-functions": "^0.4.4" 14 | }, 15 | "description": "" 16 | } 17 | -------------------------------------------------------------------------------- /functions/cors-rust/serverless.yml: -------------------------------------------------------------------------------- 1 | service: cors-rust 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | runtime: rust185 6 | 7 | plugins: 8 | - serverless-scaleway-functions 9 | 10 | package: 11 | patterns: 12 | - "!node_modules/**" 13 | - "!.gitignore" 14 | - "!.git/**" 15 | 16 | functions: 17 | cors-permissive: 18 | handler: handler_with_cors 19 | -------------------------------------------------------------------------------- /functions/cors-rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | use axum::{body::Body, extract::Request, response::Response}; 2 | use http::StatusCode; 3 | 4 | // See: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#the_http_response_headers 5 | pub fn with_permissive_cors(r: http::response::Builder) -> http::response::Builder { 6 | r.header("Access-Control-Allow-Headers", "*") 7 | .header("Access-Control-Allow-Methods", "*") 8 | .header("Access-Control-Allow-Origin", "*") 9 | } 10 | 11 | pub async fn handler_with_cors(req: Request) -> Response { 12 | println!("{:?}", req); 13 | 14 | with_permissive_cors(Response::builder()) 15 | .status(StatusCode::OK) 16 | .header("Content-Type", "text/plain") 17 | .body(Body::from("This is allowing most CORS requests")) 18 | .unwrap() 19 | } 20 | -------------------------------------------------------------------------------- /functions/go-hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.zip 3 | -------------------------------------------------------------------------------- /functions/go-hello-world/go.mod: -------------------------------------------------------------------------------- 1 | module hello-go 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/scaleway/serverless-functions-go v0.1.2 7 | github.com/stretchr/testify v1.8.2 8 | ) 9 | 10 | require ( 11 | github.com/davecgh/go-spew v1.1.1 // indirect 12 | github.com/google/uuid v1.3.0 // indirect 13 | github.com/kr/text v0.2.0 // indirect 14 | github.com/pmezard/go-difflib v1.0.0 // indirect 15 | github.com/rogpeppe/go-internal v1.11.0 // indirect 16 | gopkg.in/yaml.v3 v3.0.1 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /functions/go-hello-world/handler.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | ) 8 | 9 | func Handle(w http.ResponseWriter, r *http.Request) { 10 | _, err := io.WriteString(w, "Hello from a Go function!") 11 | 12 | if err != nil { 13 | http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /functions/go-hello-world/handler_test.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | const offlineTestingServer = "http://localhost:8080" 12 | 13 | func TestHelloWorld(t *testing.T) { 14 | resp, err := http.Get(offlineTestingServer) 15 | assert.NoError(t, err) 16 | 17 | defer resp.Body.Close() 18 | 19 | bodyBytes, err := io.ReadAll(resp.Body) 20 | assert.NoError(t, err) 21 | assert.Equal(t, "Hello from a Go function!", string(bodyBytes)) 22 | } 23 | -------------------------------------------------------------------------------- /functions/go-hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello-go", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": {}, 12 | "devDependencies": { 13 | "serverless-scaleway-functions": "^0.4.7" 14 | }, 15 | "description": "" 16 | } 17 | -------------------------------------------------------------------------------- /functions/go-hello-world/serverless.yml: -------------------------------------------------------------------------------- 1 | service: go-hello-world 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | runtime: go121 6 | 7 | plugins: 8 | - serverless-scaleway-functions 9 | 10 | package: 11 | patterns: 12 | - "!node_modules/**" 13 | - "!.gitignore" 14 | - "!.git/**" 15 | 16 | functions: 17 | hello-go: 18 | handler: "Handle" 19 | -------------------------------------------------------------------------------- /functions/go-hello-world/test/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | handler "hello-go" 5 | 6 | "github.com/scaleway/serverless-functions-go/local" 7 | ) 8 | 9 | func main() { 10 | local.ServeHandler(handler.Handle, local.WithPort(8080)) 11 | } 12 | -------------------------------------------------------------------------------- /functions/go-mail/.env: -------------------------------------------------------------------------------- 1 | SCW_DEFAULT_ORGANIZATION_ID: CHANGE_ME 2 | SCW_ACCESS_KEY: CHANGE_ME 3 | SCW_SECRET_KEY: CHANGE_ME 4 | SENDER_MAIL: CHANGE_ME -------------------------------------------------------------------------------- /functions/go-mail/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .serverless/ 4 | -------------------------------------------------------------------------------- /functions/go-mail/go.mod: -------------------------------------------------------------------------------- 1 | module gomail 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/go-playground/validator/v10 v10.17.0 7 | github.com/scaleway/scaleway-sdk-go v1.0.0-beta.22 8 | ) 9 | 10 | require ( 11 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 12 | github.com/go-playground/locales v0.14.1 // indirect 13 | github.com/go-playground/universal-translator v0.18.1 // indirect 14 | github.com/kr/pretty v0.3.1 // indirect 15 | github.com/leodido/go-urn v1.2.4 // indirect 16 | github.com/rogpeppe/go-internal v1.12.0 // indirect 17 | github.com/stretchr/testify v1.8.4 // indirect 18 | golang.org/x/crypto v0.18.0 // indirect 19 | golang.org/x/net v0.20.0 // indirect 20 | golang.org/x/sys v0.16.0 // indirect 21 | golang.org/x/text v0.14.0 // indirect 22 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 23 | gopkg.in/yaml.v2 v2.4.0 // indirect 24 | ) 25 | -------------------------------------------------------------------------------- /functions/go-mail/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "goe2e", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "test": "echo \"Error: no test specified\" && exit 1" 6 | }, 7 | "keywords": [], 8 | "author": "", 9 | "license": "ISC", 10 | "dependencies": {}, 11 | "devDependencies": { 12 | "serverless-scaleway-functions": "^0.4.9" 13 | }, 14 | "description": "" 15 | } 16 | -------------------------------------------------------------------------------- /functions/go-mail/serverless.yml: -------------------------------------------------------------------------------- 1 | service: gomail 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | runtime: go121 6 | 7 | # Enable use of .env file 8 | useDotenv: true 9 | 10 | plugins: 11 | - serverless-scaleway-functions 12 | 13 | package: 14 | patterns: 15 | - '!node_modules/**' 16 | - '!.gitignore' 17 | - '!.git/**' 18 | 19 | functions: 20 | gomail: 21 | handler: Handler 22 | privacy: private 23 | 24 | # For more details please check https://github.com/scaleway/serverless-scaleway-functions/blob/master/docs/secrets.md 25 | secret: 26 | SCW_DEFAULT_ORGANIZATION_ID: ${env:SCW_DEFAULT_ORGANIZATION_ID} 27 | SCW_ACCESS_KEY: ${env:SCW_ACCESS_KEY} 28 | SCW_SECRET_KEY: ${env:SCW_SECRET_KEY} 29 | SENDER_MAIL: ${env:SENDER_MAIL} 30 | -------------------------------------------------------------------------------- /functions/go-mnq-sqs-publish/go.mod: -------------------------------------------------------------------------------- 1 | module go-mnq-publish 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/aws/aws-sdk-go v1.44.189 7 | github.com/go-playground/validator/v10 v10.11.1 8 | github.com/scaleway/serverless-functions-go v0.1.2 9 | github.com/stretchr/testify v1.8.2 10 | ) 11 | 12 | require ( 13 | github.com/davecgh/go-spew v1.1.1 // indirect 14 | github.com/go-playground/locales v0.14.0 // indirect 15 | github.com/go-playground/universal-translator v0.18.0 // indirect 16 | github.com/google/uuid v1.3.0 // indirect 17 | github.com/jmespath/go-jmespath v0.4.0 // indirect 18 | github.com/leodido/go-urn v1.2.1 // indirect 19 | github.com/pmezard/go-difflib v1.0.0 // indirect 20 | golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect 21 | golang.org/x/sys v0.1.0 // indirect 22 | golang.org/x/text v0.4.0 // indirect 23 | gopkg.in/yaml.v3 v3.0.1 // indirect 24 | ) 25 | -------------------------------------------------------------------------------- /functions/go-mnq-sqs-publish/handler_test.go: -------------------------------------------------------------------------------- 1 | package myfunc 2 | 3 | import ( 4 | "bytes" 5 | "net/http" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | const offlineTestingServer = "http://localhost:8080" 12 | 13 | func TestHandle(t *testing.T) { 14 | 15 | body := []byte(`{ 16 | "username": "Jane Doe", 17 | "message": "Hello World" 18 | }`) 19 | resp, err := http.Post(offlineTestingServer, "application/json", bytes.NewBuffer(body)) 20 | assert.NoError(t, err) 21 | 22 | defer resp.Body.Close() 23 | 24 | assert.Equal(t, http.StatusOK, resp.StatusCode) 25 | 26 | } 27 | -------------------------------------------------------------------------------- /functions/go-mnq-sqs-publish/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "go-mnq-sqs", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "test": "echo \"Error: no test specified\" && exit 1" 6 | }, 7 | "keywords": [], 8 | "author": "", 9 | "license": "ISC", 10 | "dependencies": {}, 11 | "devDependencies": { 12 | "serverless-scaleway-functions": "^0.4.10" 13 | }, 14 | "description": "" 15 | } 16 | -------------------------------------------------------------------------------- /functions/go-mnq-sqs-publish/serverless.yml: -------------------------------------------------------------------------------- 1 | service: go-mnq-sqs 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | runtime: go121 6 | 7 | # Enable use of .env file 8 | useDotenv: true 9 | 10 | plugins: 11 | - serverless-scaleway-functions 12 | 13 | package: 14 | patterns: 15 | - '!node_modules/**' 16 | - '!.gitignore' 17 | - '!.git/**' 18 | 19 | functions: 20 | gomnq: 21 | handler: Handle 22 | 23 | # For more details please check https://github.com/scaleway/serverless-scaleway-functions/blob/master/docs/secrets.md 24 | secret: 25 | SCW_DEFAULT_ORGANIZATION_ID: ${env:SCW_DEFAULT_ORGANIZATION_ID} 26 | SQS_ACCESS_KEY: ${env:SQS_ACCESS_KEY} 27 | SQS_SECRET_KEY: ${env:SQS_SECRET_KEY} 28 | -------------------------------------------------------------------------------- /functions/go-mnq-sqs-publish/test/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | scw "go-mnq-publish" 5 | 6 | "github.com/scaleway/serverless-functions-go/local" 7 | ) 8 | 9 | func main() { 10 | local.ServeHandler(scw.Handle, local.WithPort(8080)) 11 | } 12 | -------------------------------------------------------------------------------- /functions/go-mongo/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | scw "github.com/scaleway/serverless-examples/functions/go-mongo" 5 | "github.com/scaleway/serverless-functions-go/local" 6 | ) 7 | 8 | // https://github.com/scaleway/serverless-functions-go 9 | func main() { 10 | // Replace "Handle" with your function handler name if necessary 11 | local.ServeHandler(scw.Handle, local.WithPort(8080)) 12 | } 13 | -------------------------------------------------------------------------------- /functions/go-mongo/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/scaleway/serverless-examples/functions/go-mongo 2 | 3 | go 1.24 4 | 5 | require ( 6 | github.com/scaleway/serverless-functions-go v0.1.2 7 | go.mongodb.org/mongo-driver v1.17.3 8 | ) 9 | 10 | require ( 11 | github.com/golang/snappy v1.0.0 // indirect 12 | github.com/google/go-cmp v0.7.0 // indirect 13 | github.com/google/uuid v1.6.0 // indirect 14 | github.com/klauspost/compress v1.18.0 // indirect 15 | github.com/montanaflynn/stats v0.7.1 // indirect 16 | github.com/stretchr/testify v1.10.0 // indirect 17 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect 18 | github.com/xdg-go/scram v1.1.2 // indirect 19 | github.com/xdg-go/stringprep v1.0.4 // indirect 20 | github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect 21 | golang.org/x/crypto v0.38.0 // indirect 22 | golang.org/x/sync v0.14.0 // indirect 23 | golang.org/x/text v0.25.0 // indirect 24 | ) 25 | -------------------------------------------------------------------------------- /functions/go-upload-file-s3-multipart/go.mod: -------------------------------------------------------------------------------- 1 | module file-read 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/minio/minio-go/v7 v7.0.49 7 | github.com/scaleway/serverless-functions-go v0.1.2 8 | github.com/stretchr/testify v1.8.2 9 | ) 10 | 11 | require ( 12 | github.com/davecgh/go-spew v1.1.1 // indirect 13 | github.com/dustin/go-humanize v1.0.1 // indirect 14 | github.com/google/uuid v1.3.0 // indirect 15 | github.com/json-iterator/go v1.1.12 // indirect 16 | github.com/klauspost/compress v1.16.0 // indirect 17 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 18 | github.com/kr/text v0.2.0 // indirect 19 | github.com/minio/md5-simd v1.1.2 // indirect 20 | github.com/minio/sha256-simd v1.0.0 // indirect 21 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 22 | github.com/modern-go/reflect2 v1.0.2 // indirect 23 | github.com/pmezard/go-difflib v1.0.0 // indirect 24 | github.com/rs/xid v1.4.0 // indirect 25 | github.com/sirupsen/logrus v1.9.0 // indirect 26 | golang.org/x/crypto v0.6.0 // indirect 27 | golang.org/x/net v0.7.0 // indirect 28 | golang.org/x/sys v0.6.0 // indirect 29 | golang.org/x/text v0.8.0 // indirect 30 | gopkg.in/ini.v1 v1.67.0 // indirect 31 | gopkg.in/yaml.v3 v3.0.1 // indirect 32 | ) 33 | -------------------------------------------------------------------------------- /functions/go-upload-file-s3-multipart/handler_test.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "mime/multipart" 7 | "net/http" 8 | "os" 9 | "path/filepath" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | const ( 16 | offlineTestingServer = "http://localhost:8080" 17 | fileToUpload = "go.sum" 18 | ) 19 | 20 | func TestHandle(t *testing.T) { 21 | 22 | form := new(bytes.Buffer) 23 | writer := multipart.NewWriter(form) 24 | fw, err := writer.CreateFormFile("data", filepath.Base(fileToUpload)) 25 | assert.NoError(t, err) 26 | 27 | fd, err := os.Open(fileToUpload) 28 | assert.NoError(t, err) 29 | 30 | defer fd.Close() 31 | _, err = io.Copy(fw, fd) 32 | assert.NoError(t, err) 33 | 34 | writer.Close() 35 | 36 | client := &http.Client{} 37 | req, err := http.NewRequest(http.MethodPost, offlineTestingServer, form) 38 | assert.NoError(t, err) 39 | 40 | req.Header.Set("Content-Type", writer.FormDataContentType()) 41 | resp, err := client.Do(req) 42 | assert.NoError(t, err) 43 | 44 | defer resp.Body.Close() 45 | 46 | assert.Equal(t, http.StatusOK, resp.StatusCode) 47 | 48 | } 49 | -------------------------------------------------------------------------------- /functions/go-upload-file-s3-multipart/test/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | handler "file-read" 5 | 6 | "github.com/scaleway/serverless-functions-go/local" 7 | ) 8 | 9 | func main() { 10 | // Replace "Handle" with your function handler name if necessary 11 | local.ServeHandler(handler.Handle, local.WithPort(8080)) 12 | } 13 | -------------------------------------------------------------------------------- /functions/image-labelling-node/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .serverless/ 4 | -------------------------------------------------------------------------------- /functions/image-labelling-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-scaleway-starter", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@andreekeberg/imagedata": "^1.0.2", 13 | "@tensorflow-models/mobilenet": "^2.1.0", 14 | "@tensorflow/tfjs": "^3.7.0", 15 | "aws-sdk": "^2.1314.0" 16 | }, 17 | "devDependencies": { 18 | "@scaleway/serverless-functions": "^1.0.1", 19 | "serverless-scaleway-functions": "^0.4.5" 20 | }, 21 | "description": "", 22 | "type": "module" 23 | } 24 | -------------------------------------------------------------------------------- /functions/image-labelling-node/serverless.yml: -------------------------------------------------------------------------------- 1 | service: scaleway-node18 2 | configValidationMode: off 3 | singleSource: false 4 | provider: 5 | name: scaleway 6 | runtime: node18 7 | 8 | secret: 9 | USER_ACCESS_KEY: # 10 | USER_SECRET_KEY: # 11 | S3_ENDPOINT_URL: http://s3.fr-par.scw.cloud 12 | 13 | plugins: 14 | - serverless-scaleway-functions 15 | 16 | package: 17 | patterns: 18 | - '!.gitignore' 19 | - '!.git/**' 20 | 21 | functions: 22 | image-labelling: 23 | handler: handler.handle 24 | -------------------------------------------------------------------------------- /functions/image-transform-node/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .serverless/ 4 | -------------------------------------------------------------------------------- /functions/image-transform-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scaleway-image-transform", 3 | "version": "0.0.1", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "aws-sdk": "^2.987.0", 13 | "sharp": "^0.30.7", 14 | "util": "^0.12.4" 15 | }, 16 | "devDependencies": { 17 | "@scaleway/serverless-functions": "^1.0.1", 18 | "serverless-scaleway-functions": "^0.3.0" 19 | }, 20 | "description": "" 21 | } 22 | -------------------------------------------------------------------------------- /functions/image-transform-node/serverless.yml: -------------------------------------------------------------------------------- 1 | service: scaleway-image-transform 2 | configValidationMode: off 3 | 4 | provider: 5 | name: scaleway 6 | runtime: node22 7 | 8 | secret: 9 | ACCESS_KEY: 10 | ACCESS_KEY_ID: 11 | SOURCE_BUCKET: 12 | DESTINATION_BUCKET: 13 | S3_ENDPOINT_URL: http://s3.fr-par.scw.cloud 14 | TRANSFORM_URL: 15 | 16 | scwToken: 17 | scwProject: 18 | scwRegion: fr-par 19 | 20 | plugins: 21 | - serverless-scaleway-functions 22 | 23 | patterns: 24 | - '!.gitignore' 25 | - '!.git/**' 26 | 27 | functions: 28 | imagetransform: 29 | handler: ImageTransform.handle 30 | memoryLimit: 1024 31 | minScale: 0 32 | env: 33 | RESIZED_WIDTH: '300' 34 | 35 | bucketscan: 36 | handler: BucketScan.handle 37 | memoryLimit: 1024 38 | minScale: 0 39 | events: 40 | - schedule: 41 | rate: '0 * * * *' 42 | -------------------------------------------------------------------------------- /functions/node-terraform/.gitignore: -------------------------------------------------------------------------------- 1 | # Artifacts 2 | files/ 3 | -------------------------------------------------------------------------------- /functions/node-terraform/README.md: -------------------------------------------------------------------------------- 1 | # Node Terraform 2 | 3 | An example deploying a Node Serverless function using Terraform. The function is a simple `rss` feed that filters the content of a source feed and returns the filtered content. 4 | 5 | ## Requirements 6 | 7 | - [Node.js](https://nodejs.org/en/download/) 8 | - [Terraform](https://learn.hashicorp.com/terraform/getting-started/install.html) 9 | - A configured Scaleway Profile. You can find more information [here](https://www.scaleway.com/en/docs/developer-tools/terraform/reference-content/scaleway-configuration-file/#how-to-set-up-the-configuration-file) 10 | 11 | ## Usage 12 | 13 | Run the function locally: 14 | 15 | ```bash 16 | cd function 17 | npm install --include=dev 18 | npm start 19 | ``` 20 | 21 | In another terminal, you can the following command to test the function: 22 | 23 | ```bash 24 | curl http://localhost:8081 25 | ``` 26 | 27 | Deploy the function using Terraform: 28 | 29 | ```bash 30 | terraform init 31 | terraform apply 32 | ``` 33 | 34 | The function is a `rss` feed that can be accessed via a RSS reader. The URL of the feed is displayed in the output of the Terraform apply command. 35 | 36 | ## Cleanup 37 | 38 | ```bash 39 | terraform destroy 40 | ``` 41 | -------------------------------------------------------------------------------- /functions/node-terraform/function/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-terraform", 3 | "version": "1.0.0", 4 | "description": "Node Serverless Function deployed using Scaleway's Terraform Provider", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "NODE_ENV=development node index.js" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "type": "module", 12 | "dependencies": { 13 | "rss": "^1.2.2", 14 | "rss-parser": "^3.13.0" 15 | }, 16 | "devDependencies": { 17 | "@scaleway/serverless-functions": "^1.1.2" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /functions/node-terraform/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | scaleway = { 4 | source = "scaleway/scaleway" 5 | version = "~> 2.46" 6 | } 7 | } 8 | required_version = ">= 0.13" 9 | } 10 | -------------------------------------------------------------------------------- /functions/node-upload-file-s3-multipart/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .serverless/ 4 | -------------------------------------------------------------------------------- /functions/node-upload-file-s3-multipart/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "s3-form-data", 3 | "version": "1.0.0", 4 | "main": "handler.js", 5 | "type": "module", 6 | "keywords": [], 7 | "author": "", 8 | "license": "ISC", 9 | "dependencies": { 10 | "@aws-sdk/client-s3": "^3.289.0", 11 | "parse-multipart-data": "^1.5.0" 12 | }, 13 | "devDependencies": { 14 | "@scaleway/serverless-functions": "^1.0.1", 15 | "serverless-scaleway-functions": "^0.4.4" 16 | }, 17 | "description": "" 18 | } 19 | -------------------------------------------------------------------------------- /functions/node-upload-file-s3-multipart/serverless.yml: -------------------------------------------------------------------------------- 1 | service: s3-form-data 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | runtime: node19 6 | env: 7 | BUCKET_NAME: ${env:BUCKET_NAME} 8 | S3_REGION: ${env:S3_REGION, "fr-par"} 9 | secret: 10 | ACCESS_KEY_ID: ${env:SCW_ACCESS_KEY} 11 | SECRET_KEY: ${env:SCW_SECRET_KEY} 12 | 13 | plugins: 14 | - serverless-scaleway-functions 15 | 16 | package: 17 | patterns: 18 | - "!.gitignore" 19 | - "!.git/**" 20 | 21 | functions: 22 | upload: 23 | handler: handler.uploadFormDataS3 24 | -------------------------------------------------------------------------------- /functions/php-s3/.gitignore: -------------------------------------------------------------------------------- 1 | # Function 2 | function.zip 3 | vendor/ 4 | 5 | # Terraform 6 | state 7 | state.backup 8 | .state.lock.info 9 | .terraform/ 10 | .terraform.lock.hcl 11 | 12 | # Generated 13 | curl.sh 14 | -------------------------------------------------------------------------------- /functions/php-s3/Makefile: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: tf-init 3 | tf-init: 4 | cd terraform && terraform init 5 | 6 | .PHONY: tf-plan 7 | tf-plan: 8 | cd terraform && terraform plan 9 | 10 | .PHONY: tf-apply 11 | tf-apply: 12 | cd terraform && terraform apply -auto-approve 13 | 14 | .PHONY: tf-destroy 15 | tf-destroy: 16 | cd terraform && terraform destroy 17 | -------------------------------------------------------------------------------- /functions/php-s3/function/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "aws/aws-sdk-php": "3.33" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /functions/php-s3/function/handler.php: -------------------------------------------------------------------------------- 1 | $s3_region, 21 | 'version' => 'latest', 22 | 'endpoint' => $s3_endpoint, 23 | 'credentials' => [ 24 | 'key' => $s3_access_key, 25 | 'secret' => $s3_secret, 26 | ] 27 | ]); 28 | 29 | // Write to the key 30 | $result = $s3->putObject([ 31 | 'Bucket' => $s3_bucket, 32 | 'Key' => $s3_key, 33 | 'Body' => 'This is from the PHP example', 34 | ]); 35 | 36 | // Output the result of the S3 API operation 37 | print_r($result); 38 | 39 | return [ 40 | 'body' => 'S3 function succeeded', 41 | ]; 42 | } 43 | -------------------------------------------------------------------------------- /functions/postgre-sql-node/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .serverless/ 4 | -------------------------------------------------------------------------------- /functions/postgre-sql-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postgreSQL-node-example", 3 | "version": "1.0.0", 4 | "main": "handler.js", 5 | "type": "commonjs", 6 | "keywords": [], 7 | "author": "", 8 | "license": "ISC", 9 | "dependencies": { 10 | "pg": "^8.10.0" 11 | }, 12 | "devDependencies": { 13 | "serverless-scaleway-functions": "^0.4.6" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /functions/postgre-sql-node/serverless.yml: -------------------------------------------------------------------------------- 1 | service: scaleway-node18 2 | configValidationMode: off 3 | singleSource: false 4 | provider: 5 | name: scaleway 6 | runtime: node18 7 | 8 | secret: 9 | PG_HOST: "your host IP address" 10 | PG_USER: "your database username" 11 | PG_DATABASE: "your database name" 12 | PG_PASSWORD: "your databse user password" 13 | PG_PORT: "your database port" 14 | 15 | plugins: 16 | - serverless-scaleway-functions 17 | 18 | package: 19 | patterns: 20 | - '!.gitignore' 21 | - '!.git/**' 22 | 23 | functions: 24 | get-all-from-table 25 | handler: handler.handle -------------------------------------------------------------------------------- /functions/postgre-sql-python/bin/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ./bin/deps.sh 6 | 7 | serverless deploy 8 | -------------------------------------------------------------------------------- /functions/postgre-sql-python/bin/deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # This command will run pip install with the given requirements.txt file inside 6 | # a docker container compatible with our function runtimes, and pull the installed 7 | # dependencies locally to your package directory. As these dependencies have been 8 | # installed on top of alpine Linux with our compatible system libraries, you will 9 | # be able to upload your source code and deploy your function properly. 10 | PYTHON_VERSION=3.10 11 | docker run --rm -v $(pwd):/home/app/function --workdir /home/app/function rg.fr-par.scw.cloud/scwfunctionsruntimes-public/python-dep:$PYTHON_VERSION pip install -r ./requirements.txt --target ./package 12 | -------------------------------------------------------------------------------- /functions/postgre-sql-python/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "serverless-scaleway-functions": "^0.4.6" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /functions/postgre-sql-python/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | scaleway_functions_python==0.2.0 -------------------------------------------------------------------------------- /functions/postgre-sql-python/requirements.txt: -------------------------------------------------------------------------------- 1 | psycopg2-binary == 2.9 -------------------------------------------------------------------------------- /functions/postgre-sql-python/serverless.yml: -------------------------------------------------------------------------------- 1 | service: database-python-function 2 | 3 | configValidationMode: off 4 | 5 | provider: 6 | name: scaleway 7 | runtime: python310 8 | secret: 9 | PG_HOST: "your database host IP address" 10 | PG_USER: "your database username" 11 | PG_DATABASE: "your database name" 12 | PG_PASSWORD: "your database user password" 13 | PG_PORT: "your database port" 14 | 15 | plugins: 16 | - serverless-scaleway-functions 17 | 18 | package: 19 | patterns: 20 | - '!node_modules/**' 21 | - '!.gitignore' 22 | - '!.git/**' 23 | 24 | functions: 25 | first: 26 | handler: handlers/handler.handle 27 | -------------------------------------------------------------------------------- /functions/python-chatbot/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | .serverless 4 | 5 | __pycache__/ 6 | package/ 7 | *.py[cod] 8 | *$py.class 9 | *.so 10 | -------------------------------------------------------------------------------- /functions/python-chatbot/app/english-corpus.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/functions/python-chatbot/app/english-corpus.sqlite3 -------------------------------------------------------------------------------- /functions/python-chatbot/bin/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ./bin/deps.sh 6 | 7 | serverless deploy 8 | -------------------------------------------------------------------------------- /functions/python-chatbot/bin/deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # This command will run pip install with the given requirements.txt file inside 6 | # a docker container compatible with our function runtimes, and pull the installed 7 | # dependencies locally to your package directory. As these dependencies have been 8 | # installed on top of alpine Linux with our compatible system libraries, you will 9 | # be able to upload your source code and deploy your function properly. 10 | PYTHON_VERSION=3.10 11 | docker run --rm -v $(pwd):/home/app/function --workdir /home/app/function rg.fr-par.scw.cloud/scwfunctionsruntimes-public/python-dep:$PYTHON_VERSION pip install -r ./requirements.txt --target ./package 12 | -------------------------------------------------------------------------------- /functions/python-chatbot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chatbot-python-function", 3 | "version": "1.0.0", 4 | "keywords": [], 5 | "author": "", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "serverless-scaleway-functions": "^0.4.5" 9 | }, 10 | "description": "" 11 | } 12 | -------------------------------------------------------------------------------- /functions/python-chatbot/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | scaleway_functions_python==0.2.0 -------------------------------------------------------------------------------- /functions/python-chatbot/requirements.txt: -------------------------------------------------------------------------------- 1 | pytz 2 | chatterbot==1.0.4 3 | -------------------------------------------------------------------------------- /functions/python-chatbot/serverless.yml: -------------------------------------------------------------------------------- 1 | service: chatbot-python-function 2 | 3 | provider: 4 | name: scaleway 5 | runtime: python310 6 | 7 | plugins: 8 | - serverless-scaleway-functions 9 | 10 | package: 11 | patterns: 12 | - '!node_modules/**' 13 | - '!bin/**' 14 | - '!.gitignore' 15 | - '!.idea' 16 | - '!.git/**' 17 | 18 | functions: 19 | chatbot-python: 20 | handler: app/chatbot.handle 21 | env: 22 | database_model: english-corpus.sqlite3 23 | -------------------------------------------------------------------------------- /functions/python-dependencies/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | .serverless 4 | 5 | __pycache__/ 6 | package/ 7 | *.py[cod] 8 | *$py.class 9 | *.so 10 | -------------------------------------------------------------------------------- /functions/python-dependencies/bin/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ./bin/deps.sh 6 | 7 | serverless deploy 8 | -------------------------------------------------------------------------------- /functions/python-dependencies/bin/deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | pip install -r requirements.txt --target package 6 | -------------------------------------------------------------------------------- /functions/python-dependencies/handlers/handler.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | def handle(event, context): 5 | resp = requests.get("https://www.scaleway.com") 6 | return { 7 | "body": f"Response status: {resp.status_code}", 8 | "headers": { 9 | "Content-Type": ["text/plain"], 10 | }, 11 | } 12 | 13 | if __name__ == "__main__": 14 | from scaleway_functions_python import local 15 | local.serve_handler(handle) 16 | -------------------------------------------------------------------------------- /functions/python-dependencies/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "python-deps-example", 3 | "version": "1.0.0", 4 | "keywords": [], 5 | "author": "", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "serverless-scaleway-functions": "^0.4.5" 9 | }, 10 | "description": "" 11 | } 12 | -------------------------------------------------------------------------------- /functions/python-dependencies/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | scaleway_functions_python==0.2.0 -------------------------------------------------------------------------------- /functions/python-dependencies/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | -------------------------------------------------------------------------------- /functions/python-dependencies/serverless.yml: -------------------------------------------------------------------------------- 1 | service: python-deps-example 2 | 3 | provider: 4 | name: scaleway 5 | runtime: python310 6 | 7 | plugins: 8 | - serverless-scaleway-functions 9 | 10 | package: 11 | patterns: 12 | - '!node_modules/**' 13 | 14 | functions: 15 | requests: 16 | handler: handlers/handler.handle 17 | -------------------------------------------------------------------------------- /functions/python-sqs-trigger-async-worker/function/handler.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import os 3 | 4 | import boto3 5 | 6 | 7 | def handle_front(event, context): 8 | print("received request") 9 | 10 | sqs_access_key = os.environ["SQS_ACCESS_KEY"] 11 | sqs_secret_key = os.environ["SQS_SECRET_KEY"] 12 | sqs_endpoint = os.environ["SQS_ENDPOINT"] 13 | sqs_queue_url = os.environ["SQS_QUEUE_URL"] 14 | sqs_region = os.environ["SQS_REGION"] 15 | 16 | sqs = boto3.client( 17 | "sqs", 18 | region_name=sqs_region, 19 | use_ssl=True, 20 | endpoint_url=sqs_endpoint, 21 | aws_access_key_id=sqs_access_key, 22 | aws_secret_access_key=sqs_secret_key, 23 | ) 24 | 25 | print(f"sending notification to queue {sqs_queue_url}") 26 | try: 27 | sqs.send_message( 28 | QueueUrl=sqs_queue_url, 29 | MessageBody=f"front received event at {datetime.datetime.now()}", 30 | ) 31 | except Exception as e: 32 | print(e) 33 | return {"statusCode": 500} 34 | print("sent") 35 | 36 | 37 | def handle_worker(event, context): 38 | print(f"worker received event: {event['body']}") 39 | -------------------------------------------------------------------------------- /functions/python-sqs-trigger-async-worker/function/requirements.txt: -------------------------------------------------------------------------------- 1 | # Scaleway M&Q product does not support AWS JSON protocol for now (https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-json-faqs.html), so we must use boto versions less than 1.xx.81 2 | boto3<1.28.81 3 | botocore<1.31.81 4 | -------------------------------------------------------------------------------- /functions/python-sqs-trigger-hello-world/handler.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | 4 | # This function receives an event from SQS and then randomly succeeds or fails. According to 5 | # the result, the event will be considered delivered or a retry will happen. 6 | def handle(event, context): 7 | print(f"Received body: {event['body']}") 8 | 9 | # In the case of trigger activated handlers, even if possible, it doesn't make much sense to return anything 10 | # on success. However, returning an error code will force a redelivery of the event after a delay. 11 | if random.choice([True, False]): 12 | print("Success") 13 | return 14 | else: 15 | print("Error") 16 | return {"statusCode": 500} 17 | -------------------------------------------------------------------------------- /functions/python-upload-file-s3-multipart/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | -r requirements.txt 2 | 3 | scaleway-functions-python~=0.1.0 4 | -------------------------------------------------------------------------------- /functions/python-upload-file-s3-multipart/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3==1.34.30 2 | chardet==4.0.0 3 | scw-serverless==1.2.0 4 | streaming_form_data==1.11.0 5 | -------------------------------------------------------------------------------- /functions/redis-tls/.gitignore: -------------------------------------------------------------------------------- 1 | package/ 2 | terraform/* 3 | 4 | # Only allow the configuration files 5 | !terraform/**/*.tf 6 | -------------------------------------------------------------------------------- /functions/redis-tls/README.md: -------------------------------------------------------------------------------- 1 | # Redis TLS Example 2 | 3 | An example to showcase how to connect a function to a Scaleway Redis cluster with TLS enabled. 4 | 5 | At 8:00 AM every day, the function retrieves the hourly temperature in Paris and outputs it to a Redis store. 6 | 7 | The Redis certificate is provided via a secret to the function and then written to a file. 8 | 9 | ## Requirements 10 | 11 | This example requires [Terraform](https://www.scaleway.com/en/docs/tutorials/terraform-quickstart/). 12 | 13 | ## Setup 14 | 15 | Everything is managed with Terraform. The terraform config files will also create a Redis cluster, so be sure to remove it from the configuration if you do not need one. 16 | 17 | ```sh 18 | terraform init 19 | terraform apply 20 | ``` 21 | 22 | You should be able to see your function in the Scaleway console. 23 | 24 | ## Running 25 | 26 | To check the results: 27 | 28 | - Get the Redis connection string from the console 29 | - Connect with `redis-cli`: 30 | 31 | ```sh 32 | # From a Scaleway instance (if acl enabled) 33 | redis-cli -h --user --askpass --tls --cacert serverless-weather-redis-example.pem 34 | ``` 35 | -------------------------------------------------------------------------------- /functions/redis-tls/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with python 3.10 3 | # To update, run: 4 | # 5 | # pip-compile --output-file=requirements.txt requirements.in 6 | # 7 | async-timeout==4.0.2 8 | # via redis 9 | certifi==2022.12.7 10 | # via requests 11 | charset-normalizer==2.1.1 12 | # via requests 13 | idna==3.4 14 | # via requests 15 | redis==4.4.0 16 | # via -r requirements.in 17 | requests==2.28.1 18 | # via -r requirements.in 19 | urllib3==1.26.13 20 | # via requests 21 | -------------------------------------------------------------------------------- /functions/redis-tls/terraform/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | scaleway = { 4 | source = "scaleway/scaleway" 5 | } 6 | pypi = { 7 | source = "jeffwecan/pypi" 8 | version = "0.0.11" 9 | } 10 | } 11 | required_version = ">= 0.13" 12 | } 13 | 14 | provider "scaleway" { 15 | region = "fr-par" 16 | } 17 | -------------------------------------------------------------------------------- /functions/redis-tls/terraform/store.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_redis_cluster" "weather_store" { 2 | name = "serverless-weather-redis-example" 3 | version = "7.0.5" 4 | node_type = "RED1-MICRO" 5 | user_name = var.redis_user 6 | password = var.redis_password 7 | tags = ["serverless-examples", "weather", "redis"] 8 | cluster_size = 1 9 | tls_enabled = "true" 10 | 11 | // Due to the nature of serverless functions, 12 | // the IPs we use are unpredictable. 13 | // Here we will restrict to IPs from Scaleway's AS (http://as12876.net/) 14 | dynamic "acl" { 15 | for_each = [ 16 | "62.210.0.0/16", 17 | "195.154.0.0/16", 18 | "212.129.0.0/18", 19 | "62.4.0.0/19", 20 | "212.83.128.0/19", 21 | "212.83.160.0/19", 22 | "212.47.224.0/19", 23 | "163.172.0.0/16", 24 | "51.15.0.0/16", 25 | "151.115.0.0/16", 26 | "51.158.0.0/15", 27 | ] 28 | content { 29 | ip = acl.value 30 | description = "Allow Scaleway IPs" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /functions/redis-tls/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "redis_user" { 2 | type = string 3 | } 4 | 5 | variable "redis_password" { 6 | type = string 7 | sensitive = true 8 | } 9 | -------------------------------------------------------------------------------- /functions/rust-mnist/.gitignore: -------------------------------------------------------------------------------- 1 | datasets/ 2 | debug/ 3 | target/ 4 | Cargo.lock 5 | node_modules/ 6 | package*.json 7 | !www/* 8 | -------------------------------------------------------------------------------- /functions/rust-mnist/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "function" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [[bin]] 7 | name = "test-server" 8 | 9 | [dependencies] 10 | axum = "0.8.1" 11 | tower = "0.4" 12 | tower-http = { version = "0.3.4", features = ["cors"] } 13 | tokio-test = "0.4" 14 | rust-s3 = { version = "0.32.3", default-features = false, features = [ 15 | "tokio-rustls-tls", 16 | ] } 17 | tokio = { version = "1.2", features = ["rt-multi-thread", "macros"] } 18 | anyhow = "1.0" 19 | dfdx = { version = "0.10.0" } 20 | serde = { version = "1", features = ["derive"] } 21 | serde_json = { version = "1" } 22 | pollster = "0.2.5" 23 | http = "1.3.1" 24 | 25 | [dependencies.common] 26 | path = "./common" 27 | 28 | [workspace] 29 | members = ["training", "common"] 30 | -------------------------------------------------------------------------------- /functions/rust-mnist/common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "common" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | dfdx = { version = "0.10.0", features = ["numpy"] } 10 | rust-s3 = { version = "0.32.3", default-features = false, features = [ 11 | "tokio-rustls-tls", 12 | ] } 13 | anyhow = "1.0" 14 | -------------------------------------------------------------------------------- /functions/rust-mnist/common/src/lib.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use dfdx::prelude::*; 3 | use s3::{creds::Credentials, Bucket, Region}; 4 | 5 | pub const MODEL_PATH: &str = "mnist-classifier.npz"; 6 | 7 | // our network structure 8 | pub type Mlp = ( 9 | (Linear<784, 512>, ReLU), 10 | (Linear<512, 128>, ReLU), 11 | (Linear<128, 32>, ReLU), 12 | Linear<32, 10>, 13 | ); 14 | 15 | pub fn scaleway_bucket_from_env(region: &str, bucket_name: &str) -> Result { 16 | let region = Region::Custom { 17 | region: region.to_string(), 18 | endpoint: format!("s3.{}.scw.cloud", region), 19 | }; 20 | let credentials = 21 | Credentials::from_env_specific(Some("SCW_ACCESS_KEY"), Some("SCW_SECRET_KEY"), None, None)?; 22 | 23 | Ok(Bucket::new(bucket_name, region, credentials)?) 24 | } 25 | -------------------------------------------------------------------------------- /functions/rust-mnist/serverless.yaml: -------------------------------------------------------------------------------- 1 | service: rust-examples 2 | configValidationMode: off 3 | useDotenv: true 4 | provider: 5 | name: scaleway 6 | runtime: rust185 7 | 8 | plugins: 9 | - serverless-scaleway-functions 10 | 11 | package: 12 | patterns: 13 | - "!target/**" 14 | - "!www/**" 15 | - "!training/**" 16 | - "!node_modules/**" 17 | - "!.gitignore" 18 | - "!.git/**" 19 | 20 | functions: 21 | main: 22 | handler: "handler" 23 | memoryLimit: 1024 24 | env: 25 | RUST_LOG: ${env:RUST_LOG, "info"} 26 | S3_BUCKET: ${env:S3_BUCKET} 27 | SCW_ACCESS_KEY: ${env:SCW_ACCESS_KEY} 28 | SCW_DEFAULT_REGION: ${env:SCW_DEFAULT_REGION, "fr-par"} 29 | secret: 30 | SCW_SECRET_KEY: ${env:SCW_SECRET_KEY} 31 | -------------------------------------------------------------------------------- /functions/rust-mnist/src/bin/test-server.rs: -------------------------------------------------------------------------------- 1 | use axum::{routing::post, Router}; 2 | 3 | #[tokio::main] 4 | async fn main() { 5 | let app = Router::new() 6 | .route("/", post(function::handler)); 7 | let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); 8 | axum::serve(listener, app.into_make_service()) 9 | .await 10 | .unwrap(); 11 | } 12 | -------------------------------------------------------------------------------- /functions/rust-mnist/training/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "training" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.5" 10 | mnist = "0.5.0" 11 | indicatif = "0.16.2" 12 | dfdx = { version = "0.10.0", features = ["numpy"] } 13 | tokio = { version = "1.2", features = ["rt", "macros"] } 14 | 15 | 16 | [dependencies.common] 17 | path = "../common" 18 | -------------------------------------------------------------------------------- /functions/rust-mnist/www/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-ssr 3 | logs 4 | *.log 5 | node_modules 6 | *.local 7 | -------------------------------------------------------------------------------- /functions/rust-mnist/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Serverless MNIST example 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /functions/rust-mnist/www/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "www", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@babel/core": "^7.20.2", 13 | "@emotion/react": "^11.10.5", 14 | "@emotion/styled": "^11.10.5", 15 | "@scaleway/ui": "^0.205.4", 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0" 18 | }, 19 | "devDependencies": { 20 | "@types/react": "^18.0.24", 21 | "@types/react-dom": "^18.0.8", 22 | "@vitejs/plugin-react": "^2.2.0", 23 | "typescript": "^4.6.4", 24 | "vite": "^3.2.3" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /functions/rust-mnist/www/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | height: 100vh; 7 | width: 100%; 8 | } 9 | 10 | .App { 11 | height: 100vh; 12 | } 13 | -------------------------------------------------------------------------------- /functions/rust-mnist/www/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface ImportMetaEnv { 4 | readonly VITE_SLS_FUNCTION_URL: string 5 | } 6 | 7 | interface ImportMeta { 8 | readonly env: ImportMetaEnv 9 | } 10 | -------------------------------------------------------------------------------- /functions/rust-mnist/www/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /functions/rust-mnist/www/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /functions/rust-mnist/www/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /functions/rust-mnist/www/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /functions/rust-mnist/www/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /functions/secret-manager-rotate-secret/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .serverless/ 4 | -------------------------------------------------------------------------------- /functions/secret-manager-rotate-secret/go.mod: -------------------------------------------------------------------------------- 1 | module secret-manager-rotate-secret 2 | 3 | go 1.20 4 | 5 | require github.com/scaleway/scaleway-sdk-go v1.0.0-beta.25 6 | 7 | require gopkg.in/yaml.v2 v2.4.0 // indirect 8 | -------------------------------------------------------------------------------- /functions/secret-manager-rotate-secret/go.sum: -------------------------------------------------------------------------------- 1 | github.com/scaleway/scaleway-sdk-go v1.0.0-beta.25 h1:/8rfZAdFfafRXOgz+ZpMZZWZ5pYggCY9t7e/BvjaBHM= 2 | github.com/scaleway/scaleway-sdk-go v1.0.0-beta.25/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= 3 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 4 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 5 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 6 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 7 | -------------------------------------------------------------------------------- /functions/secret-manager-rotate-secret/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secret-manager-rotate-secret", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "test": "echo \"Error: no test specified\" && exit 1" 6 | }, 7 | "keywords": [], 8 | "author": "", 9 | "license": "ISC", 10 | "dependencies": {}, 11 | "devDependencies": { 12 | "serverless-scaleway-functions": "^0.4.9" 13 | }, 14 | "description": "" 15 | } 16 | -------------------------------------------------------------------------------- /functions/secret-manager-rotate-secret/serverless.yml: -------------------------------------------------------------------------------- 1 | service: secret-manager-rotate-secret 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | runtime: go120 6 | 7 | plugins: 8 | - serverless-scaleway-functions 9 | 10 | package: 11 | patterns: 12 | - "!node_modules/**" 13 | - "!.gitignore" 14 | - "!.git/**" 15 | 16 | functions: 17 | rotate-secret: 18 | handler: "Handle" 19 | env: 20 | SCW_DEFAULT_ORGANIZATION_ID : "your scalway organization ID" 21 | SCW_DEFAULT_PROJECT_ID : "your scalway project ID" 22 | SCW_DEFAULT_REGION : "fr-par" 23 | secret: 24 | SCW_ACCESS_KEY: "your scaleway access key" 25 | SCW_SECRET_KEY: "your scaleway secret key" 26 | events: 27 | - schedule: 28 | rate: "5 4 1 * *" 29 | # Data passed as input in the request 30 | input: 31 | rdb_instance_id: "your RDB instance ID" 32 | secret_id: "the secret ID where credentials are stored" 33 | -------------------------------------------------------------------------------- /functions/serverless-gateway-python/app.py: -------------------------------------------------------------------------------- 1 | from scw_serverless import Serverless 2 | 3 | app = Serverless("serverless-api") 4 | 5 | @app.get(url="/func-a") 6 | def func_a(_event, _context): 7 | return "Hello from function A" 8 | 9 | @app.get(url="/func-b") 10 | def func_b(_event, _context): 11 | return "Hello from function B" 12 | 13 | @app.get(url="/func-c") 14 | def func_c(_event, _context): 15 | return "Hello from function C" 16 | 17 | if __name__ == "__main__": 18 | from scaleway_functions_python import local 19 | 20 | server = local.LocalFunctionServer() 21 | server.add_handler(func_a) 22 | server.add_handler(func_b) 23 | server.add_handler(func_c) 24 | server.serve(port=8080) 25 | -------------------------------------------------------------------------------- /functions/serverless-gateway-python/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | scaleway-functions-python~=0.2.0 2 | -------------------------------------------------------------------------------- /functions/serverless-gateway-python/requirements.txt: -------------------------------------------------------------------------------- 1 | scw-serverless==1.0.2 -------------------------------------------------------------------------------- /functions/serverless-gateway-python/scripts/add_function_to_gateway.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | curl -X POST https://${GATEWAY_HOST}/scw \ 5 | -H 'X-Auth-Token: ${TOKEN}' \ 6 | -H 'Content-Type: application/json' \ 7 | -d '{"target":"$1","relative_url":"$2"}' 8 | -------------------------------------------------------------------------------- /functions/serverless-gateway-python/scripts/delete_function_from_gateway.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | curl -X POST https://${GATEWAY_HOST}/scw \ 5 | -H 'X-Auth-Token: ${TOKEN}' \ 6 | -H 'Content-Type: application/json' \ 7 | -d '{"target":"$1","relative_url":"$2"}' 8 | -------------------------------------------------------------------------------- /functions/serverless-gateway-python/scripts/list_gateway_endpoints.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | curl https://${GATEWAY_HOST}/scw -H 'X-Auth-Token: ${TOKEN}' 5 | -------------------------------------------------------------------------------- /functions/terraform-python-example/function/handler.py: -------------------------------------------------------------------------------- 1 | import os 2 | from urllib import request,parse,error 3 | import json 4 | 5 | auth_token=os.environ['X-AUTH-TOKEN'] 6 | 7 | def handle(event, context): 8 | # Get information from cron 9 | event_body=eval(event["body"]) 10 | zone=event_body["zone"] 11 | server_id=event_body["server_id"] 12 | action=event_body["action"] # action should be "poweron" or "poweroff" 13 | 14 | # Create request 15 | url=f"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}/action" 16 | data=json.dumps({"action":action}).encode('ascii') 17 | req = request.Request(url, data=data, method="POST") 18 | req.add_header('Content-Type', 'application/json') 19 | req.add_header('X-Auth-Token',auth_token) 20 | 21 | # Sending request to Instance API 22 | try: 23 | res=request.urlopen(req).read().decode() 24 | except error.HTTPError as e: 25 | res=e.read().decode() 26 | 27 | return { 28 | "body": json.loads(res), 29 | "statusCode": 200, 30 | } 31 | -------------------------------------------------------------------------------- /functions/terraform-python-example/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | scaleway = { 4 | source = "scaleway/scaleway" 5 | version = "2.52.0" 6 | } 7 | } 8 | required_version = ">= 0.13" 9 | } 10 | -------------------------------------------------------------------------------- /functions/terraform-python-example/terraform.tfvars: -------------------------------------------------------------------------------- 1 | zone = "fr-par-1" 2 | region = "fr-par" 3 | env = "dev" 4 | project_id = "" 5 | -------------------------------------------------------------------------------- /functions/terraform-python-example/variables.tf: -------------------------------------------------------------------------------- 1 | variable "zone" { 2 | type = string 3 | } 4 | 5 | variable "region" { 6 | type = string 7 | } 8 | 9 | variable "env" { 10 | type = string 11 | } 12 | 13 | variable "project_id" { 14 | type = string 15 | description = "Your project ID." 16 | } 17 | -------------------------------------------------------------------------------- /functions/trigger-image-transform-node/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/functions/trigger-image-transform-node/.DS_Store -------------------------------------------------------------------------------- /functions/trigger-image-transform-node/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .serverless/ 4 | serverless.yml_local -------------------------------------------------------------------------------- /functions/trigger-image-transform-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "s3-sqs-triggers", 3 | "version": "1.0.0", 4 | "keywords": [], 5 | "author": "", 6 | "license": "ISC", 7 | "dependencies": { 8 | "@aws-sdk/client-s3": "^3.325.0", 9 | "@aws-sdk/client-sqs": "^3.325.0", 10 | "sharp": "^0.32.1" 11 | }, 12 | "devDependencies": { 13 | "@scaleway/serverless-functions": "^1.0.1", 14 | "serverless-scaleway-functions": "^0.4.6" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # Generated files 5 | *.zip 6 | 7 | # .tfstate files 8 | *.tfstate 9 | *.tfstate.* 10 | 11 | # Crash log files 12 | crash.log 13 | crash.*.log 14 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/go/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "triggers-getting-started/go" 5 | "github.com/scaleway/serverless-functions-go/local" 6 | ) 7 | 8 | func main() { 9 | // Replace "Handle" with your function handler name if necessary 10 | local.ServeHandler(handler.Handle, local.WithPort(8080)) 11 | } 12 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/go/go.mod: -------------------------------------------------------------------------------- 1 | module triggers-getting-started/go 2 | 3 | go 1.20 4 | 5 | require github.com/scaleway/serverless-functions-go v0.1.2 6 | 7 | require github.com/google/uuid v1.3.0 // indirect 8 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 3 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 5 | github.com/scaleway/serverless-functions-go v0.1.2 h1:UUToB+XXpLDG9l6c9c0ALhAs1jvb6rV2Lkyj2kZEYCo= 6 | github.com/scaleway/serverless-functions-go v0.1.2/go.mod h1:23Hj74DYzTsVY3QgvsM0MGnx0+0OAAabTC06BOMJE58= 7 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 8 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 9 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/nats.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_mnq_nats_account" "main" { 2 | name = "nats-account" 3 | } 4 | 5 | resource "scaleway_mnq_nats_credentials" "main" { 6 | account_id = scaleway_mnq_nats_account.main.id 7 | name = "triggers-nats" 8 | } 9 | 10 | resource "local_sensitive_file" "nats" { 11 | filename = "triggers-nats.creds" 12 | content = scaleway_mnq_nats_credentials.main.file 13 | } 14 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/node/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "triggers-getting-started", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "handler.js", 6 | "scripts": { 7 | "start": "node handler.js" 8 | }, 9 | "type": "module", 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@scaleway/serverless-functions": "^1.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/outputs.tf: -------------------------------------------------------------------------------- 1 | output "sqs_access_key" { 2 | value = scaleway_mnq_sqs_credentials.main.access_key 3 | sensitive = true 4 | } 5 | 6 | output "sqs_secret_key" { 7 | value = scaleway_mnq_sqs_credentials.main.secret_key 8 | sensitive = true 9 | } 10 | 11 | output "subject_name" { 12 | value = local.subject_name 13 | } 14 | 15 | output "creds_file" { 16 | value = local_sensitive_file.nats.filename 17 | } 18 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/php/handler.php: -------------------------------------------------------------------------------- 1 | 405, 18 | "headers" => ["Content-Type" => "text/plain"], 19 | "body" => "Method Not Allowed", 20 | ]; 21 | } 22 | 23 | # The content of the SQS message is passed in the body. 24 | $n = intval($event["body"]); 25 | $result = factorial($n); 26 | 27 | echo "php: factorial of $n is $result\n"; 28 | 29 | return [ 30 | // If the status code is not in the 2XX range, the message is considered 31 | // failed and is retried. In total, there are 3 retries. 32 | "statusCode" => 200, 33 | "headers" => ["Content-Type" => "text/plain"], 34 | // Because triggers are asynchronous, the response body is ignored. 35 | // It's kept here when testing locally. 36 | "body" => $result, 37 | ]; 38 | } 39 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | scaleway = { 4 | source = "scaleway/scaleway" 5 | version = ">= 2.31" 6 | } 7 | archive = { 8 | source = "hashicorp/archive" 9 | version = ">= 2.4" 10 | } 11 | } 12 | required_version = ">= 0.13" 13 | } 14 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/python/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | scaleway_functions_python==0.2.0 2 | requests==2.28.2 3 | pytest==7.2.2 -------------------------------------------------------------------------------- /functions/triggers-getting-started/rust/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "triggers-getting-started" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | axum = "0.8.1" 10 | http = "1.3.1" 11 | hyper = { version = "0.14", features = ["http1"] } 12 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/sqs.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_mnq_sqs_credentials" "main" { 2 | name = "triggers-getting-started" 3 | permissions { 4 | can_publish = true 5 | can_receive = true 6 | can_manage = true 7 | } 8 | } 9 | 10 | resource "scaleway_mnq_sqs_queue" "main" { 11 | for_each = local.functions 12 | 13 | name = "factorial-requests-${each.key}" 14 | 15 | access_key = scaleway_mnq_sqs_credentials.main.access_key 16 | secret_key = scaleway_mnq_sqs_credentials.main.secret_key 17 | } 18 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/tests/requirements.txt: -------------------------------------------------------------------------------- 1 | # Scaleway M&Q product does not support AWS JSON protocol for now (https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-json-faqs.html), so we must use boto versions less than 1.xx.81 2 | boto3<1.28.81 3 | botocore<1.31.81 4 | requests~=2.31.0 5 | nats-py==2.6.0 6 | nkeys==0.1.0 7 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/tests/send_nats_messages.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import asyncio 4 | import nats 5 | 6 | MAX_FACTORIAL_REQUESTS = 20 7 | 8 | # Credentials file generated by Terraform 9 | NATS_CREDS_FILE = os.environ["NATS_CREDS_FILE"] 10 | NATS_ENDPOINT_URL = "nats://nats.mnq.fr-par.scaleway.com:4222" 11 | SUBJECT_NAME = os.environ["NATS_SUBJECT"] 12 | 13 | async def send_nats(): 14 | nc = await nats.connect(NATS_ENDPOINT_URL, user_credentials=NATS_CREDS_FILE) 15 | 16 | print(f"Sending 0..{MAX_FACTORIAL_REQUESTS} factorial requests to {SUBJECT_NAME}...") 17 | for i in range(MAX_FACTORIAL_REQUESTS): 18 | await nc.publish(SUBJECT_NAME, f"{i}".encode()) 19 | 20 | await nc.close() 21 | 22 | 23 | if __name__ == "__main__": 24 | asyncio.run(send_nats()) 25 | -------------------------------------------------------------------------------- /functions/triggers-getting-started/tests/send_sqs_messages.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import boto3 4 | 5 | # Credentials generated by Terraform 6 | AWS_ACCESS_KEY_ID = os.environ["AWS_ACCESS_KEY_ID"] 7 | AWS_SECRET_ACCESS_KEY = os.environ["AWS_SECRET_ACCESS_KEY"] 8 | 9 | MAX_FACTORIAL_REQUESTS = 20 10 | 11 | params = { 12 | "endpoint_url": "https://sqs.mnq.fr-par.scw.cloud", 13 | "aws_access_key_id": AWS_ACCESS_KEY_ID, 14 | "aws_secret_access_key": AWS_SECRET_ACCESS_KEY, 15 | "region_name": "fr-par", 16 | } 17 | 18 | client = boto3.client("sqs", **params) 19 | sqs = boto3.resource("sqs", **params) 20 | 21 | 22 | def main(): 23 | queues = client.list_queues() 24 | for queue_url in queues["QueueUrls"]: 25 | queue = sqs.Queue(queue_url) 26 | queue_name = queue.attributes["QueueArn"].split(":")[-1] 27 | print(f"Sending 0..{MAX_FACTORIAL_REQUESTS} factorial requests to {queue_name}...") 28 | for i in range(MAX_FACTORIAL_REQUESTS): 29 | # See: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sqs/queue/index.html 30 | queue.send_message(MessageBody=str(i)) 31 | 32 | 33 | if __name__ == "__main__": 34 | main() 35 | -------------------------------------------------------------------------------- /functions/triggers-nats/.gitignore: -------------------------------------------------------------------------------- 1 | # Nats credentials 2 | files/nats-creds 3 | 4 | # Local .terraform directories 5 | **/.terraform/* 6 | 7 | # .tfstate files 8 | *.tfstate 9 | *.tfstate.* 10 | 11 | # Crash log files 12 | crash.log 13 | crash.*.log 14 | -------------------------------------------------------------------------------- /functions/triggers-nats/files/function.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/functions/triggers-nats/files/function.zip -------------------------------------------------------------------------------- /functions/triggers-nats/function/handler.py: -------------------------------------------------------------------------------- 1 | def handle(event, context): 2 | # The content of the NATS message is passed in the body 3 | msg_body = event.get("body") 4 | print(f"Received message: {msg_body}") 5 | 6 | return "Hello NATS!" 7 | -------------------------------------------------------------------------------- /functions/triggers-nats/main.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_function_namespace" "main" { 2 | name = "triggers-nats" 3 | } 4 | 5 | data "archive_file" "source_zip" { 6 | type = "zip" 7 | source_dir = "${path.module}/function" 8 | output_path = "${path.module}/files/function.zip" 9 | } 10 | 11 | resource "scaleway_function" "main" { 12 | namespace_id = scaleway_function_namespace.main.id 13 | name = "nats-target" 14 | runtime = "python310" 15 | handler = "handler.handle" 16 | 17 | zip_file = data.archive_file.source_zip.output_path 18 | zip_hash = filesha256(data.archive_file.source_zip.output_path) 19 | 20 | privacy = "public" 21 | 22 | deploy = true 23 | 24 | memory_limit = 512 # MB / 280 mVCPU 25 | 26 | min_scale = 0 27 | } 28 | 29 | resource "scaleway_function_trigger" "main" { 30 | function_id = scaleway_function.main.id 31 | name = "nats-trigger" 32 | nats { 33 | account_id = scaleway_mnq_nats_account.main.id 34 | subject = "triggers-nats-topic" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /functions/triggers-nats/messaging.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_mnq_nats_account" "main" { 2 | name = "nats-account" 3 | } 4 | 5 | resource "scaleway_mnq_nats_credentials" "main" { 6 | account_id = scaleway_mnq_nats_account.main.id 7 | name = "triggers-nats" 8 | } 9 | 10 | resource "local_sensitive_file" "nats" { 11 | filename = "files/nats-creds" 12 | content = "${scaleway_mnq_nats_credentials.main.file}" 13 | } 14 | -------------------------------------------------------------------------------- /functions/triggers-nats/outputs.tf: -------------------------------------------------------------------------------- 1 | output "nats_endpoint" { 2 | value = scaleway_mnq_nats_account.main.endpoint 3 | sensitive = true 4 | } 5 | 6 | -------------------------------------------------------------------------------- /functions/triggers-nats/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | scaleway = { 4 | source = "scaleway/scaleway" 5 | version = ">= 2.31" 6 | } 7 | archive = { 8 | source = "hashicorp/archive" 9 | version = ">= 2.4" 10 | } 11 | } 12 | required_version = ">= 0.13" 13 | } 14 | -------------------------------------------------------------------------------- /functions/triggers-nats/tests/requirements.txt: -------------------------------------------------------------------------------- 1 | asyncio~=3.4.3 2 | nats-py~=2.5.0 3 | nkeys~=0.1.0 4 | requests~=2.31.0 5 | -------------------------------------------------------------------------------- /functions/triggers-nats/tests/send_messages.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import os 3 | 4 | import nats 5 | 6 | NATS_ENDPOINT = os.environ.get("NATS_ENDPOINT") 7 | NATS_CREDS_PATH = "../files/nats-creds" 8 | 9 | NATS_TOPIC = "triggers-nats-topic" 10 | 11 | 12 | async def main(): 13 | print(f"Connecting to NATS account at {NATS_ENDPOINT}") 14 | 15 | # Connect to NATS 16 | nc = await nats.connect(servers=[NATS_ENDPOINT], user_credentials=NATS_CREDS_PATH) 17 | 18 | # Publish a message 19 | await nc.publish(NATS_TOPIC, b"Hello from the NATS trigger!") 20 | 21 | # Close NATS connection 22 | await nc.close() 23 | 24 | 25 | if __name__ == "__main__": 26 | asyncio.run(main()) 27 | -------------------------------------------------------------------------------- /functions/typescript-with-node/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .serverless/ -------------------------------------------------------------------------------- /functions/typescript-with-node/handler.ts: -------------------------------------------------------------------------------- 1 | export {handle}; 2 | 3 | function handle(event: Record, context: Record, cb: unknown) { 4 | return { 5 | body: "Hello world!", 6 | headers: { "Content-Type": ["application/json"] }, 7 | statusCode: 200, 8 | }; 9 | }; 10 | 11 | /* This is used to test locally and will not be executed on Scaleway Functions */ 12 | if (process.env.NODE_ENV === 'test') { 13 | import("@scaleway/serverless-functions").then(scw_fnc_node => { 14 | scw_fnc_node.serveHandler(handle, 8080); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /functions/typescript-with-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-with-node", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@types/node": "^18.15.11", 14 | "@scaleway/serverless-functions": "^1.0.1", 15 | "serverless-scaleway-functions": "^0.4.5", 16 | "typescript": "^5.0.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /functions/typescript-with-node/serverless.yml: -------------------------------------------------------------------------------- 1 | service: typescript-hello-world 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | runtime: node18 6 | 7 | plugins: 8 | - serverless-scaleway-functions 9 | 10 | package: 11 | patterns: 12 | - "!node_modules/**" 13 | - "!.gitignore" 14 | - "!.git/**" 15 | 16 | functions: 17 | hello-world: 18 | handler: handler.handle -------------------------------------------------------------------------------- /jobs/instances-snapshot-cleaner/Dockerfile: -------------------------------------------------------------------------------- 1 | # Using apline/golang image 2 | FROM golang:1.24-alpine 3 | 4 | # Set destination for COPY 5 | WORKDIR /app 6 | 7 | # Copy required files 8 | COPY go.mod ./ 9 | COPY go.sum ./ 10 | COPY *.go ./ 11 | 12 | # Build the executable 13 | RUN go build -o /jobs-snapshot-cleaner 14 | 15 | # Run the executable 16 | ENTRYPOINT [ "/jobs-snapshot-cleaner" ] 17 | -------------------------------------------------------------------------------- /jobs/instances-snapshot-cleaner/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/scaleway/serverless-examples/jobs/instances-snapshot 2 | 3 | go 1.24 4 | 5 | require github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 6 | 7 | require ( 8 | github.com/kr/pretty v0.3.1 // indirect 9 | github.com/rogpeppe/go-internal v1.14.1 // indirect 10 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 11 | gopkg.in/yaml.v2 v2.4.0 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /jobs/instances-snapshot/Dockerfile: -------------------------------------------------------------------------------- 1 | # Using apline/golang image 2 | FROM golang:1.24-alpine 3 | 4 | # Set destination for COPY 5 | WORKDIR /app 6 | 7 | # Copy required files 8 | COPY go.mod ./ 9 | COPY go.sum ./ 10 | COPY *.go ./ 11 | 12 | # Build the executable 13 | RUN go build -o /jobs-snapshot 14 | 15 | # Run the executable 16 | ENTRYPOINT [ "/jobs-snapshot" ] 17 | -------------------------------------------------------------------------------- /jobs/instances-snapshot/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/scaleway/serverless-examples/jobs/instances-snapshot 2 | 3 | go 1.24 4 | 5 | require github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 6 | 7 | require ( 8 | github.com/kr/pretty v0.3.1 // indirect 9 | github.com/rogpeppe/go-internal v1.14.1 // indirect 10 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 11 | gopkg.in/yaml.v2 v2.4.0 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /jobs/ml-ops/data/.gitignore: -------------------------------------------------------------------------------- 1 | dataset/ 2 | -------------------------------------------------------------------------------- /jobs/ml-ops/data/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12-slim-bookworm 2 | 3 | WORKDIR /app 4 | 5 | RUN apt-get update 6 | RUN apt-get install -y \ 7 | curl \ 8 | unzip 9 | 10 | RUN pip install --upgrade pip 11 | COPY requirements.txt . 12 | RUN pip install -r requirements.txt 13 | 14 | COPY . . 15 | 16 | CMD ["python", "main.py"] 17 | -------------------------------------------------------------------------------- /jobs/ml-ops/data/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3==1.33.2 2 | requests==2.31.0 3 | -------------------------------------------------------------------------------- /jobs/ml-ops/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | data: 5 | build: 6 | context: ./data 7 | depends_on: 8 | - minio 9 | environment: 10 | - ACCESS_KEY=example 11 | - SECRET_KEY=example-password 12 | - REGION=foo 13 | - S3_BUCKET_NAME=mlops 14 | - S3_URL=http://minio:9000 15 | 16 | training: 17 | build: 18 | context: ./training 19 | depends_on: 20 | - minio 21 | environment: 22 | - ACCESS_KEY=example 23 | - SECRET_KEY=example-password 24 | - REGION=foo 25 | - S3_BUCKET_NAME=mlops 26 | - S3_URL=http://minio:9000 27 | 28 | inference: 29 | build: 30 | context: ./inference 31 | ports: 32 | - 8080:80 33 | depends_on: 34 | - minio 35 | environment: 36 | - ACCESS_KEY=example 37 | - SECRET_KEY=example-password 38 | - REGION=foo 39 | - S3_BUCKET_NAME=mlops 40 | - S3_URL=http://minio:9000 41 | 42 | minio: 43 | image: minio/minio 44 | ports: 45 | - "9000:9000" 46 | volumes: 47 | - minio_storage:/data 48 | environment: 49 | - MINIO_ROOT_USER=example 50 | - MINIO_ROOT_PASSWORD=example-password 51 | entrypoint: sh 52 | command: -c 'mkdir -p /data/mlops && /usr/bin/minio server /data' 53 | 54 | volumes: 55 | minio_storage: {} 56 | -------------------------------------------------------------------------------- /jobs/ml-ops/inference/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12-slim-bookworm 2 | 3 | WORKDIR /app 4 | 5 | RUN pip install --upgrade pip 6 | COPY requirements.txt . 7 | RUN pip install -r requirements.txt 8 | 9 | COPY . . 10 | CMD ["uvicorn", "main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] 11 | -------------------------------------------------------------------------------- /jobs/ml-ops/inference/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "age": 44, 3 | "job": "blue-collar", 4 | "marital": "married", 5 | "education": "basic.4y", 6 | "default": "unknown", 7 | "housing": "yes", 8 | "loan": "no", 9 | "contact": "cellular", 10 | "month": "aug", 11 | "day_of_week": "thu", 12 | "duration": 210, 13 | "campaign": 1, 14 | "pdays": 999, 15 | "previous": "0", 16 | "poutcome": "nonexistent", 17 | "emp_var_rate": 1.4, 18 | "cons_price_idx": 93.444, 19 | "cons_conf_idx": -36.1, 20 | "euribor3m": 4.963, 21 | "nr_employed": 5228.1 22 | } 23 | -------------------------------------------------------------------------------- /jobs/ml-ops/inference/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi==0.104.1 2 | boto3==1.33.2 3 | uvicorn==0.24.0.post1 4 | pandas==2.1.2 5 | numpy==1.26.2 6 | scikit-learn==1.3.2 7 | -------------------------------------------------------------------------------- /jobs/ml-ops/terraform/container.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_container_namespace" "main" { 2 | name = "ml-ops-example-${random_string.random_suffix.result}" 3 | description = "MLOps example" 4 | } 5 | 6 | resource "scaleway_container" "inference" { 7 | name = "inference" 8 | description = "Inference serving API" 9 | namespace_id = scaleway_container_namespace.main.id 10 | registry_image = docker_image.inference.name 11 | port = 80 12 | cpu_limit = 2000 13 | memory_limit = 2048 14 | min_scale = 1 15 | max_scale = 1 16 | environment_variables = { 17 | "S3_BUCKET_NAME" = scaleway_object_bucket.main.name 18 | "S3_URL" = var.s3_url 19 | "REGION" = var.region 20 | } 21 | secret_environment_variables = { 22 | "ACCESS_KEY" = var.access_key 23 | "SECRET_KEY" = var.secret_key 24 | } 25 | deploy = true 26 | } 27 | 28 | resource scaleway_container_cron "inference_cron" { 29 | container_id = scaleway_container.inference.id 30 | schedule = var.inference_cron_schedule 31 | args = jsonencode({}) 32 | } -------------------------------------------------------------------------------- /jobs/ml-ops/terraform/images.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_registry_namespace" "main" { 2 | name = "ml-ops-example-${random_string.random_suffix.result}" 3 | region = var.region 4 | project_id = var.project_id 5 | } 6 | 7 | resource "docker_image" "inference" { 8 | name = "${scaleway_registry_namespace.main.endpoint}/inference:${var.image_version}" 9 | build { 10 | context = "${path.cwd}/../inference" 11 | } 12 | 13 | provisioner "local-exec" { 14 | command = "docker push ${docker_image.inference.name}" 15 | } 16 | } 17 | 18 | resource "docker_image" "data" { 19 | name = "${scaleway_registry_namespace.main.endpoint}/data:${var.image_version}" 20 | build { 21 | context = "${path.cwd}/../data" 22 | } 23 | 24 | provisioner "local-exec" { 25 | command = "docker push ${docker_image.data.name}" 26 | } 27 | } 28 | 29 | resource "docker_image" "training" { 30 | name = "${scaleway_registry_namespace.main.endpoint}/training:${var.image_version}" 31 | build { 32 | context = "${path.cwd}/../training" 33 | } 34 | 35 | provisioner "local-exec" { 36 | command = "docker push ${docker_image.training.name}" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /jobs/ml-ops/terraform/jobs.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_job_definition" "fetch_data" { 2 | name = "fetch_data" 3 | cpu_limit = 1000 4 | memory_limit = 1024 5 | image_uri = docker_image.data.name 6 | timeout = "10m" 7 | cron { 8 | schedule = var.data_fetch_cron_schedule 9 | timezone = "Europe/Paris" 10 | } 11 | env = { 12 | "S3_BUCKET_NAME" : scaleway_object_bucket.main.name, 13 | "S3_URL" : var.s3_url, 14 | "ACCESS_KEY" : var.access_key, 15 | "SECRET_KEY" : var.secret_key, 16 | "REGION" : var.region 17 | } 18 | } 19 | 20 | resource "scaleway_job_definition" "training" { 21 | name = "training" 22 | cpu_limit = 4000 23 | memory_limit = 4096 24 | image_uri = docker_image.training.name 25 | timeout = "10m" 26 | cron { 27 | schedule = var.training_cron_schedule 28 | timezone = "Europe/Paris" 29 | } 30 | env = { 31 | "S3_BUCKET_NAME" : scaleway_object_bucket.main.name, 32 | "S3_URL" : var.s3_url, 33 | "ACCESS_KEY" : var.access_key, 34 | "SECRET_KEY" : var.secret_key, 35 | "REGION" : var.region, 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /jobs/ml-ops/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | 2 | output "endpoint" { 3 | value = scaleway_container.inference.domain_name 4 | } 5 | 6 | output "training_job_id" { 7 | value = scaleway_job_definition.training.id 8 | } 9 | 10 | output "fetch_data_job_id" { 11 | value = scaleway_job_definition.fetch_data.id 12 | } 13 | -------------------------------------------------------------------------------- /jobs/ml-ops/terraform/providers.tf: -------------------------------------------------------------------------------- 1 | provider "scaleway" { 2 | region = var.region 3 | access_key = var.access_key 4 | secret_key = var.secret_key 5 | project_id = var.project_id 6 | } 7 | 8 | provider "docker" { 9 | host = "unix:///var/run/docker.sock" 10 | 11 | registry_auth { 12 | address = scaleway_registry_namespace.main.endpoint 13 | username = "nologin" 14 | password = var.secret_key 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /jobs/ml-ops/terraform/s3.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_object_bucket" "main" { 2 | name = "ml-ops-${random_string.random_suffix.result}" 3 | } 4 | -------------------------------------------------------------------------------- /jobs/ml-ops/terraform/utils.tf: -------------------------------------------------------------------------------- 1 | resource "random_string" "random_suffix" { 2 | length = 8 3 | upper = false 4 | special = false 5 | } 6 | -------------------------------------------------------------------------------- /jobs/ml-ops/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "access_key" { 2 | type = string 3 | } 4 | 5 | variable "secret_key" { 6 | type = string 7 | } 8 | 9 | variable "project_id" { 10 | type = string 11 | } 12 | 13 | variable "image_version" { 14 | type = string 15 | default = "0.0.3" 16 | } 17 | 18 | variable "region" { 19 | type = string 20 | default = "fr-par" 21 | } 22 | 23 | variable "s3_url" { 24 | type = string 25 | default = "https://s3.fr-par.scw.cloud" 26 | } 27 | 28 | variable "data_fetch_cron_schedule" { 29 | type = string 30 | default = "0 */10 * * *" 31 | } 32 | 33 | variable "training_cron_schedule" { 34 | type = string 35 | default = "0 */11 * * *" 36 | } 37 | 38 | variable "inference_cron_schedule" { 39 | type = string 40 | default = "0 */12 * * *" 41 | } 42 | -------------------------------------------------------------------------------- /jobs/ml-ops/terraform/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | scaleway = { 4 | source = "scaleway/scaleway" 5 | version = ">= 2.39" 6 | } 7 | docker = { 8 | source = "kreuzwerker/docker" 9 | version = "3.0.2" 10 | } 11 | } 12 | required_version = ">= 0.13" 13 | } 14 | -------------------------------------------------------------------------------- /jobs/ml-ops/training/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12-slim-bookworm 2 | 3 | WORKDIR /app 4 | 5 | RUN pip install --upgrade pip 6 | COPY requirements.txt . 7 | RUN pip install -r requirements.txt 8 | 9 | COPY . . 10 | CMD [ "python", "main.py" ] 11 | -------------------------------------------------------------------------------- /jobs/ml-ops/training/requirements.txt: -------------------------------------------------------------------------------- 1 | pandas==2.1.3 2 | numpy==1.26.2 3 | scikit-learn==1.3.2 4 | imblearn==0.0 5 | matplotlib==3.8.2 6 | boto3==1.33.2 7 | -------------------------------------------------------------------------------- /jobs/registry-empty-ressource-cleaner/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the alpine version of the golang image as the base image 2 | FROM golang:1.24-alpine 3 | 4 | # Set the working directory inside the container to /app 5 | WORKDIR /app 6 | 7 | # Copy the go.mod and go.sum files to the working directory 8 | COPY go.mod ./ 9 | COPY go.sum ./ 10 | 11 | # Copy the Go source files to the working directory 12 | COPY *.go ./ 13 | 14 | # Build the executable named reg-clean from the Go source files 15 | RUN go build -o /reg-namespace-clean 16 | # Set the default command to run the reg-clean executable when the container starts 17 | 18 | CMD ["/reg-namespace-clean"] 19 | -------------------------------------------------------------------------------- /jobs/registry-empty-ressource-cleaner/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/scaleway/serverless-examples/jobs/registry-empty-ressource-cleaner 2 | 3 | go 1.24.0 4 | 5 | require github.com/scaleway/scaleway-sdk-go v1.0.0-beta.32 6 | 7 | require ( 8 | github.com/kr/pretty v0.3.1 // indirect 9 | github.com/rogpeppe/go-internal v1.13.1 // indirect 10 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 11 | gopkg.in/yaml.v2 v2.4.0 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /jobs/registry-version-based-retention/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the alpine version of the golang image as the base image 2 | FROM golang:1.24-alpine 3 | 4 | # Set the working directory inside the container to /app 5 | WORKDIR /app 6 | 7 | # Copy the go.mod and go.sum files to the working directory 8 | COPY go.mod ./ 9 | COPY go.sum ./ 10 | 11 | # Copy the Go source files to the working directory 12 | COPY *.go ./ 13 | 14 | # Build the executable named reg-clean from the Go source files 15 | RUN go build -o /reg-clean 16 | 17 | # Set the default command to run the reg-clean executable when the container starts 18 | CMD ["/reg-clean"] 19 | -------------------------------------------------------------------------------- /jobs/registry-version-based-retention/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/scaleway/serverless-examples/jobs/registry-version-based-retention 2 | 3 | go 1.24.0 4 | 5 | require github.com/scaleway/scaleway-sdk-go v1.0.0-beta.32 6 | 7 | require ( 8 | github.com/kr/pretty v0.3.1 // indirect 9 | github.com/rogpeppe/go-internal v1.13.1 // indirect 10 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 11 | gopkg.in/yaml.v2 v2.4.0 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /jobs/terraform-hello-world/image/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.19 2 | 3 | RUN apk add --no-cache --upgrade bash 4 | 5 | COPY hello.sh . 6 | 7 | CMD ["sh", "hello.sh"] 8 | -------------------------------------------------------------------------------- /jobs/terraform-hello-world/image/hello.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | echo "-- Job run at $(date) --" 6 | echo "$MESSAGE" 7 | -------------------------------------------------------------------------------- /jobs/terraform-hello-world/terraform/images.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_registry_namespace" "main" { 2 | name = "jobs-tf-hello" 3 | region = var.region 4 | project_id = var.project_id 5 | } 6 | 7 | resource "docker_image" "main" { 8 | name = "${scaleway_registry_namespace.main.endpoint}/jobs-hello:${var.image_version}" 9 | build { 10 | context = "${path.cwd}/../image" 11 | } 12 | 13 | provisioner "local-exec" { 14 | command = "docker push ${docker_image.main.name}" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /jobs/terraform-hello-world/terraform/jobs.tf: -------------------------------------------------------------------------------- 1 | resource "scaleway_job_definition" "main" { 2 | name = "hello_jobs" 3 | cpu_limit = 1000 4 | memory_limit = 1024 5 | 6 | image_uri = docker_image.main.name 7 | 8 | command = "sh hello.sh" 9 | 10 | env = { 11 | "MESSAGE" : "Hello from your Job!", 12 | } 13 | 14 | cron { 15 | schedule = "*/5 * * * *" 16 | timezone = "Europe/Paris" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /jobs/terraform-hello-world/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "job_id" { 2 | value = scaleway_job_definition.main.id 3 | } 4 | 5 | -------------------------------------------------------------------------------- /jobs/terraform-hello-world/terraform/providers.tf: -------------------------------------------------------------------------------- 1 | provider "scaleway" { 2 | region = var.region 3 | access_key = var.access_key 4 | secret_key = var.secret_key 5 | project_id = var.project_id 6 | } 7 | 8 | provider "docker" { 9 | host = "unix:///var/run/docker.sock" 10 | 11 | registry_auth { 12 | address = scaleway_registry_namespace.main.endpoint 13 | username = "nologin" 14 | password = var.secret_key 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /jobs/terraform-hello-world/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "access_key" { 2 | type = string 3 | } 4 | 5 | variable "secret_key" { 6 | type = string 7 | } 8 | 9 | variable "project_id" { 10 | type = string 11 | } 12 | 13 | variable "image_version" { 14 | type = string 15 | default = "0.0.3" 16 | } 17 | 18 | variable "region" { 19 | type = string 20 | default = "fr-par" 21 | } 22 | 23 | -------------------------------------------------------------------------------- /jobs/terraform-hello-world/terraform/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | scaleway = { 4 | source = "scaleway/scaleway" 5 | version = ">= 2.38.2" 6 | } 7 | docker = { 8 | source = "kreuzwerker/docker" 9 | version = "3.0.2" 10 | } 11 | } 12 | required_version = ">= 0.13" 13 | } 14 | -------------------------------------------------------------------------------- /mnq/large-messages/function/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3 2 | img2pdf 3 | -------------------------------------------------------------------------------- /mnq/large-messages/upload_img.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | SCW_BUCKET=$(terraform output bucket_name) 4 | SCW_NATS_URL=$(terraform output nats_url) 5 | 6 | # Nats context creation and selection 7 | nats context save large-messages --server=$SCW_NATS_URL --creds=./large-messages.creds 8 | nats context select large-messages 9 | 10 | # Upload file to S3 11 | aws s3 cp $1 s3://$SCW_BUCKET 12 | 13 | # Send the name of the file in NATS 14 | nats pub large-messages $(basename $1) 15 | -------------------------------------------------------------------------------- /mnq/serverless-scraping/.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | .env 3 | *.zip 4 | package/ 5 | 6 | # terraform 7 | **/.terraform/* 8 | 9 | *.tfstate 10 | *.tfstate.* 11 | 12 | crash.log 13 | crash.*.log 14 | 15 | *.tfvars 16 | *.tfvars.json 17 | -------------------------------------------------------------------------------- /mnq/serverless-scraping/archives/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory except this file 2 | !.gitignore -------------------------------------------------------------------------------- /mnq/serverless-scraping/consumer/requirements.txt: -------------------------------------------------------------------------------- 1 | pg8000 2 | requests 3 | bs4 -------------------------------------------------------------------------------- /mnq/serverless-scraping/scraper/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3 2 | bs4 3 | requests -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/publisher-server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bookworm-20240211 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y golang ca-certificates && \ 5 | update-ca-certificates && \ 6 | rm -rf /var/lib/apt/lists/* 7 | 8 | WORKDIR /app 9 | 10 | COPY ./cmd ./cmd 11 | COPY ./internal ./internal 12 | COPY ./www ./www 13 | 14 | RUN go mod init publisher-server && \ 15 | go mod tidy && \ 16 | go build ./cmd/publisher 17 | 18 | EXPOSE 8081 19 | 20 | CMD ["./publisher"] -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/publisher-server/cmd/publisher/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "log" 6 | "net/http" 7 | "publisher-server/internal/handlers" 8 | "publisher-server/internal/sns_client" 9 | ) 10 | 11 | func main() { 12 | sns_client.InitAwsSnsClient() 13 | 14 | router := gin.Default() 15 | router.LoadHTMLGlob("./www/templates/*") 16 | router.Static("/", "./www") 17 | router.POST("/low-CPU-usage", handlers.LowCPUUsageHandler) 18 | router.POST("/high-CPU-usage", handlers.HighCPUUsageHandler) 19 | 20 | router.NoRoute(func(context *gin.Context) { 21 | context.HTML(http.StatusNotFound, "message.html", gin.H{ 22 | "Title": "404 Not Found", 23 | "Message": "Oh no 😢 The tutorial lost you", 24 | }) 25 | }) 26 | 27 | log.Println("Publisher-server listening on port 8081 ...") 28 | if err := router.Run(":8081"); err != nil { 29 | log.Fatalf("Failed to start server: %v", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/publisher-server/internal/handlers/high_cpu_usage.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "log" 6 | "net/http" 7 | "publisher-server/internal/utils" 8 | ) 9 | 10 | func HighCPUUsageHandler(context *gin.Context) { 11 | if err := utils.PublishMessage("Alert: 'publisher-server' CPU usage is above 70%"); err != nil { 12 | log.Printf("Error publishing message: %v", err) 13 | context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to publish message"}) 14 | return 15 | } 16 | 17 | context.JSON(http.StatusOK, gin.H{"message": "High CPU Usage simulated"}) 18 | } 19 | -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/publisher-server/internal/handlers/low_cpu_usage.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "log" 6 | "net/http" 7 | "publisher-server/internal/utils" 8 | ) 9 | 10 | func LowCPUUsageHandler(context *gin.Context) { 11 | if err := utils.PublishMessage("Notice: 'publisher-server' CPU usage normalized to 30%"); err != nil { 12 | 13 | log.Printf("Error publishing message: %v", err) 14 | context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to publish message"}) 15 | return 16 | } 17 | 18 | context.JSON(http.StatusOK, gin.H{"message": "Low CPU Usage simulated"}) 19 | } 20 | -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/publisher-server/internal/sns_client/client.go: -------------------------------------------------------------------------------- 1 | package sns_client 2 | 3 | import ( 4 | "github.com/aws/aws-sdk-go/aws" 5 | "github.com/aws/aws-sdk-go/aws/credentials" 6 | "github.com/aws/aws-sdk-go/aws/session" 7 | "github.com/aws/aws-sdk-go/service/sns" 8 | "os" 9 | ) 10 | 11 | var AwsSns *sns.SNS 12 | 13 | func InitAwsSnsClient() { 14 | awsSession := session.Must(session.NewSession(&aws.Config{ 15 | Region: aws.String("fr-par"), 16 | Endpoint: aws.String("http://sns.mnq.fr-par.scaleway.com"), 17 | Credentials: credentials.NewStaticCredentials(os.Getenv("AWS_ACCESS_KEY"), os.Getenv("AWS_SECRET_KEY"), ""), 18 | })) 19 | 20 | AwsSns = sns.New(awsSession) 21 | } 22 | -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/publisher-server/internal/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "github.com/aws/aws-sdk-go/aws" 6 | "github.com/aws/aws-sdk-go/service/sns" 7 | "log" 8 | "os" 9 | "publisher-server/internal/sns_client" 10 | ) 11 | 12 | func PublishMessage(message string) error { 13 | _, err := sns_client.AwsSns.Publish(&sns.PublishInput{ 14 | Message: aws.String(message), 15 | TopicArn: aws.String(os.Getenv("TOPIC_ARN")), 16 | }) 17 | if err != nil { 18 | return fmt.Errorf("publish message to topic failed: %w", err) 19 | } 20 | 21 | log.Println("Message published successfully") 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/subscriber-server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bookworm-20240211 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y golang ca-certificates && \ 5 | update-ca-certificates && \ 6 | rm -rf /var/lib/apt/lists/* 7 | 8 | WORKDIR /app 9 | 10 | COPY ./cmd ./cmd 11 | COPY ./internal ./internal 12 | COPY ./www ./www 13 | 14 | RUN go mod init subscriber-server && \ 15 | go mod tidy && \ 16 | go build -o ./subscriber ./cmd/subscriber 17 | 18 | EXPOSE 8081 19 | 20 | CMD ["./subscriber"] -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/subscriber-server/cmd/subscriber/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "subscriber-server/internal/handlers" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | func main() { 11 | router := gin.Default() 12 | 13 | router.LoadHTMLGlob("./www/templates/*") 14 | 15 | router.GET("/", handlers.GetIndexHandler) 16 | router.GET("/confirm-subscription", handlers.ConfirmSubscriptionHandler) 17 | router.POST("/notifications", handlers.SnsServiceHandler) 18 | router.POST("/confirm-subscription", handlers.ConfirmClickHandler) 19 | router.GET("/notifications", handlers.GetNotificationsHandler) 20 | router.GET("/notifications-refresh", handlers.RefreshNotificationsHandler) 21 | 22 | router.NoRoute(handlers.NoRouteHandler) 23 | 24 | fmt.Println("Subscriber-server listening on port 8081 ...") 25 | if err := router.Run(":8081"); err != nil { 26 | panic("Error when listening on port 8081: " + err.Error()) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/subscriber-server/internal/app/app_state.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "subscriber-server/internal/types" 5 | "sync" 6 | ) 7 | 8 | // SharedState /* State holding the confirmation state and the notifications */ 9 | var SharedState = AppState{ 10 | ReceivedConfirmation: types.Confirmation{}, 11 | FormattedNotifications: make([]string, 0), 12 | } 13 | 14 | type AppState struct { 15 | ReceivedConfirmation types.Confirmation 16 | ConfirmationMutex sync.Mutex 17 | FormattedNotifications []string 18 | NotificationMutex sync.Mutex 19 | } 20 | -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/subscriber-server/internal/handlers/common.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func NoRouteHandler(context *gin.Context) { 10 | context.HTML(http.StatusNotFound, "message.html", gin.H{ 11 | "Title": "404 Not Found", 12 | "Message": "Oh no 😢 The tutorial lost you", 13 | }) 14 | } 15 | 16 | func GetIndexHandler(context *gin.Context) { 17 | context.File("./www/index.html") 18 | } -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/subscriber-server/internal/handlers/notifications.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "subscriber-server/internal/app" 7 | ) 8 | 9 | func GetNotificationsHandler(context *gin.Context) { 10 | app.SharedState.NotificationMutex.Lock() 11 | defer app.SharedState.NotificationMutex.Unlock() 12 | context.HTML(http.StatusOK, "notifications.html", gin.H{ 13 | "FormattedNotifications": app.SharedState.FormattedNotifications, 14 | }) 15 | } 16 | 17 | func RefreshNotificationsHandler(context *gin.Context) { 18 | app.SharedState.NotificationMutex.Lock() 19 | defer app.SharedState.NotificationMutex.Unlock() 20 | context.JSON(http.StatusOK, gin.H{ 21 | "notifications": app.SharedState.FormattedNotifications, 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/subscriber-server/internal/handlers/sns_service.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "subscriber-server/internal/utils" 7 | ) 8 | 9 | func SnsServiceHandler(context *gin.Context) { 10 | messageType := context.GetHeader("x-amz-sns-message-type") 11 | 12 | switch messageType { 13 | case "SubscriptionConfirmation": 14 | utils.GetSubscribeURL(context) 15 | case "Notification": 16 | utils.FormatNotification(context) 17 | default: 18 | context.JSON(http.StatusBadRequest, gin.H{"error": "Unsupported message type"}) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/subscriber-server/internal/types/confirmation.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type Confirmation struct { 4 | SubscribeURL string 5 | Confirmed bool 6 | } 7 | -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/subscriber-server/internal/types/notification.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type Notification struct { 4 | Subject string `json:"Subject"` 5 | Message string `json:"Message"` 6 | } 7 | -------------------------------------------------------------------------------- /mnq/sns-instances-notification-system/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "public_ssh_key" { 2 | description = "Public SSH Key to access your instances" 3 | type = string 4 | } -------------------------------------------------------------------------------- /projects/blogpost-glacier/caas/.env: -------------------------------------------------------------------------------- 1 | # Generate keys : https://www.scaleway.com/en/docs/console/my-project/how-to/generate-api-key/ 2 | SCW_SECRET_KEY=YOUR_SECRET_KEY 3 | SCW_DEFAULT_PROJECT_ID=YOUR_PROJECT_KEY 4 | 5 | # S3 Scaleway doc : https://www.scaleway.com/en/docs/storage/object/concepts/ 6 | S3_ENDPOINT= # sample.s3.fr-par.scw.cloud 7 | S3_ACCESSKEY=YOUR_S3_ACCESS_KEY 8 | S3_SECRET=YOUR_S3_SECRET_KEY 9 | 10 | # DB Postgres Scaleway doc : https://www.scaleway.com/en/docs/storage/object/concepts/ 11 | DB_HOST="1.1.1.1" 12 | DB_PORT="12345" 13 | DB_USER=sample 14 | DB_PASS=samplePassword 15 | DB_NAME=sample 16 | -------------------------------------------------------------------------------- /projects/blogpost-glacier/caas/my-container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.19-alpine 2 | 3 | # Set destination for COPY 4 | WORKDIR /app 5 | 6 | # Download Go modules 7 | COPY go.mod ./ 8 | COPY go.sum ./ 9 | RUN go mod download 10 | 11 | # Copy the source code. Note the slash at the end, as explained in 12 | # https://docs.docker.com/engine/reference/builder/#copy 13 | COPY *.go ./ 14 | COPY *.html ./ 15 | 16 | # Build 17 | RUN go build -o /server-image 18 | 19 | HEALTHCHECK --interval=10s --timeout=10s --start-period=5s CMD curl -f http://127.0.0.1:8080/status || exit 1 20 | 21 | # This is for documentation purposes only. 22 | # To actually open the port, runtime parameters 23 | # must be supplied to the docker command. 24 | EXPOSE 8080 25 | 26 | # (Optional) environment variable that our dockerised 27 | # application can make use of. The value of environment 28 | # variables can also be set via parameters supplied 29 | # to the docker command on the command line. 30 | #ENV HTTP_PORT=8081 31 | 32 | # Run 33 | CMD [ "/server-image" ] 34 | -------------------------------------------------------------------------------- /projects/blogpost-glacier/caas/my-container/go.mod: -------------------------------------------------------------------------------- 1 | module gitlab.infra.online.net/ttacquet/blogpost-glaclier/caas 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/lib/pq v1.10.7 7 | github.com/minio/minio-go/v7 v7.0.39 8 | ) 9 | 10 | require ( 11 | github.com/dustin/go-humanize v1.0.0 // indirect 12 | github.com/google/uuid v1.3.0 // indirect 13 | github.com/json-iterator/go v1.1.12 // indirect 14 | github.com/klauspost/compress v1.15.11 // indirect 15 | github.com/klauspost/cpuid/v2 v2.1.1 // indirect 16 | github.com/minio/md5-simd v1.1.2 // indirect 17 | github.com/minio/sha256-simd v1.0.0 // indirect 18 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 19 | github.com/modern-go/reflect2 v1.0.2 // indirect 20 | github.com/rs/xid v1.4.0 // indirect 21 | github.com/sirupsen/logrus v1.9.0 // indirect 22 | github.com/stretchr/testify v1.8.0 // indirect 23 | golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b // indirect 24 | golang.org/x/net v0.0.0-20221004154528-8021a29435af // indirect 25 | golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect 26 | golang.org/x/text v0.3.7 // indirect 27 | gopkg.in/ini.v1 v1.67.0 // indirect 28 | ) 29 | -------------------------------------------------------------------------------- /projects/blogpost-glacier/caas/my-container/layout.html: -------------------------------------------------------------------------------- 1 | {{.PageTitle}} 2 | 3 | {{range .Glaciers}} 4 | Image : {{.ImageURI}} : Download 5 | {{end}} 6 | 7 | -------------------------------------------------------------------------------- /projects/blogpost-glacier/caas/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "caas", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": {}, 12 | "devDependencies": { 13 | "serverless-scaleway-functions": "^0.4.2" 14 | }, 15 | "description": "" 16 | } 17 | -------------------------------------------------------------------------------- /projects/blogpost-glacier/caas/serverless.yml: -------------------------------------------------------------------------------- 1 | service: caas 2 | configValidationMode: off 3 | useDotenv: true 4 | provider: 5 | name: scaleway 6 | scwToken: ${env:SCW_SECRET_KEY} 7 | scwProject: ${env:SCW_DEFAULT_PROJECT_ID} 8 | 9 | plugins: 10 | - serverless-scaleway-functions 11 | 12 | package: 13 | patterns: 14 | - '!node_modules/**' 15 | - '!.gitignore' 16 | - '!.git/**' 17 | 18 | custom: 19 | containers: 20 | glacier: 21 | directory: my-container 22 | minScale: 0 23 | memoryLimit: 512 24 | maxScale: 1 25 | maxConcurrency: 50 26 | port: 8080 27 | secret: 28 | S3_ENDPOINT: ${env:S3_ENDPOINT} 29 | S3_ACCESSKEY: ${env:S3_ACCESSKEY} 30 | S3_SECRET: ${env:S3_SECRET} 31 | DB_HOST: ${env:DB_HOST} 32 | DB_PORT: ${env:DB_PORT} 33 | DB_USER: ${env:DB_USER} 34 | DB_PASS: ${env:DB_PASS} 35 | DB_NAME: ${env:DB_NAME} 36 | -------------------------------------------------------------------------------- /projects/blogpost-glacier/faas/.env: -------------------------------------------------------------------------------- 1 | # Generate keys : https://www.scaleway.com/en/docs/console/my-project/how-to/generate-api-key/ 2 | SCW_SECRET_KEY=YOUR_SECRET_KEY 3 | SCW_DEFAULT_PROJECT_ID=YOUR_PROJECT_KEY 4 | 5 | # S3 Scaleway doc : https://www.scaleway.com/en/docs/storage/object/concepts/ 6 | S3_ENDPOINT= # sample.s3.fr-par.scw.cloud 7 | S3_ACCESSKEY=YOUR_S3_ACCESS_KEY 8 | S3_SECRET=YOUR_S3_SECRET_KEY 9 | 10 | # DB Postgres Scaleway doc : https://www.scaleway.com/en/docs/storage/object/concepts/ 11 | DB_HOST="1.1.1.1" 12 | DB_PORT="12345" 13 | DB_USER=sample 14 | DB_PASS=samplePassword 15 | DB_NAME=sample 16 | -------------------------------------------------------------------------------- /projects/blogpost-glacier/faas/go.mod: -------------------------------------------------------------------------------- 1 | module myhandler 2 | 3 | go 1.22 4 | 5 | require github.com/minio/minio-go/v7 v7.0.39 6 | 7 | require ( 8 | github.com/dustin/go-humanize v1.0.0 // indirect 9 | github.com/google/uuid v1.3.0 // indirect 10 | github.com/json-iterator/go v1.1.12 // indirect 11 | github.com/klauspost/compress v1.15.11 // indirect 12 | github.com/klauspost/cpuid/v2 v2.1.1 // indirect 13 | github.com/minio/md5-simd v1.1.2 // indirect 14 | github.com/minio/sha256-simd v1.0.0 // indirect 15 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 16 | github.com/modern-go/reflect2 v1.0.2 // indirect 17 | github.com/rs/xid v1.4.0 // indirect 18 | github.com/sirupsen/logrus v1.9.0 // indirect 19 | github.com/stretchr/testify v1.8.0 // indirect 20 | golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b // indirect 21 | golang.org/x/net v0.0.0-20221004154528-8021a29435af // indirect 22 | golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect 23 | golang.org/x/text v0.3.7 // indirect 24 | gopkg.in/ini.v1 v1.67.0 // indirect 25 | ) 26 | -------------------------------------------------------------------------------- /projects/blogpost-glacier/faas/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "faas", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "test": "echo \"Error: no test specified\" && exit 1" 6 | }, 7 | "keywords": [], 8 | "author": "", 9 | "license": "ISC", 10 | "dependencies": {}, 11 | "devDependencies": { 12 | "serverless-scaleway-functions": "^0.4.2" 13 | }, 14 | "description": "" 15 | } 16 | -------------------------------------------------------------------------------- /projects/blogpost-glacier/faas/serverless.yml: -------------------------------------------------------------------------------- 1 | service: faas 2 | configValidationMode: off 3 | useDotenv: true 4 | provider: 5 | name: scaleway 6 | runtime: go122 7 | scwToken: ${env:SCW_SECRET_KEY} 8 | scwProject: ${env:SCW_DEFAULT_PROJECT_ID} 9 | 10 | plugins: 11 | - serverless-scaleway-functions 12 | 13 | package: 14 | patterns: 15 | - '!node_modules/**' 16 | - '!.gitignore' 17 | - '!.git/**' 18 | 19 | functions: 20 | glacier: 21 | handler: Handle 22 | events: 23 | - schedule: 24 | rate: '0 14 * * *' 25 | secret: 26 | S3_ENDPOINT: ${env:S3_ENDPOINT} 27 | S3_ACCESSKEY: ${env:S3_ACCESSKEY} 28 | S3_SECRET: ${env:S3_SECRET} 29 | DB_HOST: ${env:DB_HOST} 30 | DB_PORT: ${env:DB_PORT} 31 | DB_USER: ${env:DB_USER} 32 | DB_PASS: ${env:DB_PASS} 33 | DB_NAME: ${env:DB_NAME} 34 | -------------------------------------------------------------------------------- /projects/blogpost-glacier/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@serverless/compose": "^1.3.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /projects/blogpost-glacier/serverless-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | faas: 3 | path: faas 4 | caas: 5 | path: caas 6 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/Makefile: -------------------------------------------------------------------------------- 1 | deploy: node_modules 2 | serverless deploy 3 | 4 | node_modules: 5 | npm i 6 | 7 | clean: remove 8 | 9 | remove: 10 | - serverless remove --service=apiGateway 11 | - serverless remove --service=getToken 12 | - serverless remove --service=myApp 13 | 14 | dist-clean: remove 15 | - rm -rf node_modules package-lock.json 16 | - find . -name .serverless -exec rm -rf "{}" + 17 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/apiGateway/kong/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM kong:alpine 2 | 3 | USER root 4 | 5 | COPY kong.yml.template /kong.yml.template 6 | COPY kong.conf /kong.conf 7 | COPY start.sh /start.sh 8 | 9 | RUN chmod a+w /kong.yml.template \ 10 | && chmod +x /start.sh \ 11 | && apk add --no-cache \ 12 | tini \ 13 | gettext \ 14 | && rm -rf \ 15 | /var/cache/* \ 16 | /root/.cache/* \ 17 | /docker-entrypoint.sh 18 | 19 | ENV KONG_NGINX_DAEMON=off 20 | 21 | ENTRYPOINT ["/start.sh"] 22 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/apiGateway/kong/kong.conf: -------------------------------------------------------------------------------- 1 | database = off 2 | admin_listen = off 3 | declarative_config = /kong.yml 4 | proxy_listen = 0.0.0.0:8000 reuseport backlog=16384 5 | log_level = info 6 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/apiGateway/kong/kong.yml.template: -------------------------------------------------------------------------------- 1 | _format_version: '2.1' 2 | _transform: true 3 | consumers: 4 | - plugins: 5 | - config: 6 | hour: 10000 7 | policy: local 8 | second: 5 9 | name: rate-limiting 10 | username: appuser 11 | routes: 12 | - name: default-route 13 | paths: 14 | - / 15 | service: commands 16 | - name: orders 17 | paths: 18 | - /orders 19 | service: orders 20 | services: 21 | - name: commands 22 | url: https://${COMMANDS_URL} 23 | - name: orders 24 | url: https://${ORDERS_URL} 25 | plugins: 26 | - name: request-transformer 27 | config: 28 | add: 29 | headers: 30 | - X-Auth-Token:${TOKEN} 31 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/apiGateway/kong/start.sh: -------------------------------------------------------------------------------- 1 | #!/sbin/tini /bin/sh 2 | echo "Parsing kong.yml template" 3 | envsubst < /kong.yml.template > /kong.yml 4 | echo "Starting Kong API Gateway DB-less" 5 | /usr/local/bin/kong start -v -c /kong.conf 6 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/apiGateway/serverless.yml: -------------------------------------------------------------------------------- 1 | service: apiGateway 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | scwRegion: nl-ams 6 | 7 | plugins: 8 | - serverless-scaleway-functions 9 | 10 | package: 11 | patterns: 12 | - '!node_modules/**' 13 | - '!.gitignore' 14 | - '!.git/**' 15 | - '!Makefile' 16 | 17 | custom: 18 | containers: 19 | kong: 20 | directory: kong 21 | minScale: 1 22 | memoryLimit: 512 23 | port: 8000 24 | env: 25 | COMMANDS_URL: ${param:commandUrl} 26 | ORDERS_URL: ${param:orderUrl} 27 | secret: 28 | TOKEN: ${param:token} 29 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/getToken/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test -f token && echo "Token already exists, re-using." && exit 0 3 | echo "Generating token in namespace $NAMESPACE_ID in region $REGION_NAME" 4 | curl -sSL -X POST -H "X-Auth-Token: $SCW_SECRET_KEY" -H 'Content-Type: application/json' -d "{\"namespace_id\": \"$NAMESPACE_ID\"}" https://api.scaleway.com/functions/v1beta1/regions/$REGION_NAME/tokens | jq .token | sed 's/"//g' > token 5 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/getToken/info.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo -e "Stack Outputs:\n token: $(cat token)\n" 3 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/getToken/remove.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -f token 3 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/getToken/serverless.yml: -------------------------------------------------------------------------------- 1 | provider: 2 | name: Custom 3 | environment: 4 | NAMESPACE_ID: ${param:namespace} 5 | REGION_NAME: ${param:region} 6 | 7 | service: getToken 8 | configValidationMode: off 9 | 10 | plugins: 11 | - ./plugin.js 12 | 13 | package: 14 | patterns: 15 | - 'token' 16 | - '!plugin.js' 17 | - '!node_modules/**' 18 | - '!.gitignore' 19 | - '!.git/**' 20 | - '!Makefile' 21 | 22 | custom: 23 | scripts: 24 | commands: 25 | deploy: ./deploy.sh 26 | info: ./info.sh 27 | remove: ./remove.sh 28 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/getToken/token: -------------------------------------------------------------------------------- 1 | eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBsaWNhdGlvbl9jbGFpbSI6W3sibmFtZXNwYWNlX2lkIjoiYmQxZThkMWUtMWRkYi00YTkyLWI1NjYtN2Y5MzEwNmNjYmUxIiwiYXBwbGljYXRpb25faWQiOiIifV0sInZlcnNpb24iOjIsImF1ZCI6ImZ1bmN0aW9ucyIsImp0aSI6IjljOGQ0MTExLTkzZTctNDc2OC1hMDc2LTAxMWE5NmM3NzllZSIsImlhdCI6MTY2ODU0ODc5NCwiaXNzIjoiU0NBTEVXQVkiLCJuYmYiOjE2Njg1NDg3OTQsInN1YiI6InRva2VuIn0.mTXRx6wHN5hvlcaoRigfzZSW4Q9nvAouV5eexJ8Z727qC9UYTLPs3jzfsIDU_Y5IUR5RqEhLPLXnHOoGvk5zKhs9Rp7NGxEgXlk_1LhMU1vhftS29S98hDrKa1RGSGmhbJC4HNVPSUp5p4VivRCiGiHG06umw1BNgQxjPaNCcpz_iZA5l6HFeAGtnLPfFnuY9-ZhpqS0JE1ldn00M_-sHa2ONBM5qBwtAVxiSlNlVAjn4_g1afk3qkTp00SeYVoyobl1daXSFIYJQiF4HsgZVx34TLJGcAFM49F07dP-w7vijWC_ZE1mY9F8OzEQifAdANYUEy4Q_m13A08IHj0AQGyUcXEg0l10Cnkmv0NmTpGCYZPHTi0PRnMMlGWh9LyL6AvuP_NSmq76FTLUkB0jZGnxioPELtiki_PpCbIgrdoD4iNbCQMHR1FEof_UUu49_JLzwjXgZnsKPD6EBxUnRalQ7JUoAeE5DGGUA4GTBoVQ98Vj-IEsgzIQqnt3Nn92iyBtOpPOD9pSCB4jQaOrdmjkVINLwU39eseHKA2nb33LtQwPrPaiZiRyQtpRaLa3OrfoMdzzgI6zKDvLQ2BAeHgURAbmI3_1aFLa1XBoad-Z3zY-0sqh8k8KP4fUoqT1kJZaTOtZ3SFIgawZVWtwFbjAyUTFWe7365VVCIR7byU 2 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/myApp/commands.py: -------------------------------------------------------------------------------- 1 | from jinja2 import Environment, DictLoader, select_autoescape 2 | 3 | def command(event, context): 4 | index_html = """ 5 | 6 | 7 | commands 8 | Check orders 9 | 10 | 11 | """ 12 | env = Environment( 13 | loader=DictLoader({'index.html': index_html}), 14 | autoescape=select_autoescape() 15 | ) 16 | template = env.get_template("index.html") 17 | return { 18 | "body": template.render(), 19 | "statusCode": 200, 20 | } 21 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/myApp/orders.py: -------------------------------------------------------------------------------- 1 | from jinja2 import Environment, DictLoader, select_autoescape 2 | 3 | def order(event, context): 4 | index_html = """ 5 | 6 | 7 | orders 8 | 9 | 10 | """ 11 | env = Environment( 12 | loader=DictLoader({'index.html': index_html}), 13 | autoescape=select_autoescape() 14 | ) 15 | template = env.get_template("index.html") 16 | return { 17 | "body": template.render(), 18 | "statusCode": 200, 19 | } 20 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/myApp/serverless.yml: -------------------------------------------------------------------------------- 1 | service: myApp 2 | configValidationMode: off 3 | provider: 4 | name: scaleway 5 | scwRegion: nl-ams 6 | runtime: python310 7 | 8 | plugins: 9 | - serverless-scaleway-functions 10 | 11 | package: 12 | patterns: 13 | - '!node_modules/**' 14 | - '!.gitignore' 15 | - '!.git/**' 16 | - '!Makefile' 17 | 18 | functions: 19 | orders: 20 | handler: orders.order 21 | memoryLimit: 256 22 | minScale: 1 23 | privacy: private 24 | commands: 25 | handler: commands.command 26 | memoryLimit: 256 27 | minScale: 1 28 | privacy: private 29 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fullApp", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "@serverless/compose": "^1.2.4", 13 | "serverless-scaleway-functions": ">=0.4.0" 14 | }, 15 | "description": "" 16 | } 17 | -------------------------------------------------------------------------------- /projects/kong-api-gateway/serverless-compose.yml: -------------------------------------------------------------------------------- 1 | # serverless-compose.yml 2 | services: 3 | myApp: 4 | path: myApp 5 | getToken: 6 | path: getToken 7 | params: 8 | namespace: ${myApp.functions.commands.namespace_id} 9 | region: ${myApp.functions.commands.region} 10 | dependsOn: 11 | - myApp 12 | apiGateway: 13 | path: apiGateway 14 | params: 15 | token: ${getToken.token} 16 | commandUrl: ${myApp.functions.commands.domain_name} 17 | orderUrl: ${myApp.functions.orders.domain_name} 18 | dependsOn: 19 | - myApp 20 | - getToken 21 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/.dockerignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | .yarn/install-state.gz 6 | 7 | # testing 8 | /coverage 9 | 10 | # next.js 11 | /.next/ 12 | /out/ 13 | 14 | # production 15 | /build 16 | 17 | # misc 18 | .DS_Store 19 | *.pem 20 | 21 | # debug 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # local env files 27 | .env*.local 28 | 29 | # vercel 30 | .vercel 31 | 32 | # typescript 33 | *.tsbuildinfo 34 | next-env.d.ts 35 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM node:20-alpine 4 | 5 | WORKDIR /usr/app 6 | COPY ./ ./ 7 | RUN npm install 8 | RUN npm run build 9 | 10 | #Web application configuration 11 | ENV PORT=8080 12 | 13 | #Database configuration used for dynamically rendered data 14 | ENV PGHOST=localhost 15 | ENV PGPORT=5432 16 | ENV PGDATABASE=database 17 | ENV PGUSER=user 18 | ENV PGPASSWORD=password 19 | 20 | CMD npm run start -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 Vercel, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "classnames": "^2.5.1", 10 | "date-fns": "^3.3.1", 11 | "gray-matter": "^4.0.3", 12 | "next": "14.1.0", 13 | "pg": "^8.11.3", 14 | "react": "^18", 15 | "react-dom": "^18", 16 | "remark": "^15.0.1", 17 | "remark-html": "^16.0.1" 18 | }, 19 | "devDependencies": { 20 | "@types/node": "^20", 21 | "@types/pg": "^8.11.0", 22 | "@types/react": "^18", 23 | "@types/react-dom": "^18", 24 | "autoprefixer": "^10.0.1", 25 | "postcss": "^8", 26 | "tailwindcss": "^3.3.0", 27 | "typescript": "^5" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/assets/blog/authors/jj.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/public/assets/blog/authors/jj.jpeg -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/assets/blog/authors/joe.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/public/assets/blog/authors/joe.jpeg -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/assets/blog/authors/tim.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/public/assets/blog/authors/tim.jpeg -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/assets/blog/dynamic-routing/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/public/assets/blog/dynamic-routing/cover.jpg -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/assets/blog/hello-world/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/public/assets/blog/hello-world/cover.jpg -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/assets/blog/preview/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/public/assets/blog/preview/cover.jpg -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/public/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/public/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/public/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #000000 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/public/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/public/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/public/favicon/favicon.ico -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/public/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/public/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Next.js", 3 | "short_name": "Next.js", 4 | "icons": [ 5 | { 6 | "src": "/favicons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/favicons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#000000", 17 | "background_color": "#000000", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/_components/avatar.tsx: -------------------------------------------------------------------------------- 1 | type Props = { 2 | name: string; 3 | picture: string; 4 | }; 5 | 6 | const Avatar = ({ name, picture }: Props) => { 7 | return ( 8 | 9 | 10 | {name} 11 | 12 | ); 13 | }; 14 | 15 | export default Avatar; 16 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/_components/container.tsx: -------------------------------------------------------------------------------- 1 | type Props = { 2 | children?: React.ReactNode; 3 | }; 4 | 5 | const Container = ({ children }: Props) => { 6 | return {children}; 7 | }; 8 | 9 | export default Container; 10 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/_components/cover-image.tsx: -------------------------------------------------------------------------------- 1 | import cn from "classnames"; 2 | import Link from "next/link"; 3 | import Image from "next/image"; 4 | 5 | type Props = { 6 | title: string; 7 | src: string; 8 | slug?: string; 9 | }; 10 | 11 | const CoverImage = ({ title, src, slug }: Props) => { 12 | const image = ( 13 | 22 | ); 23 | return ( 24 | 25 | {slug ? ( 26 | 27 | {image} 28 | 29 | ) : ( 30 | image 31 | )} 32 | 33 | ); 34 | }; 35 | 36 | export default CoverImage; 37 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/_components/date-formatter.tsx: -------------------------------------------------------------------------------- 1 | import { parseISO, format } from "date-fns"; 2 | 3 | type Props = { 4 | dateString: string; 5 | }; 6 | 7 | const DateFormatter = ({ dateString }: Props) => { 8 | const date = parseISO(dateString); 9 | return {format(date, "LLLL d, yyyy")}; 10 | }; 11 | 12 | export default DateFormatter; 13 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/_components/header.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | 3 | const Header = () => { 4 | return ( 5 | 6 | 7 | Blog 8 | 9 | . 10 | 11 | ); 12 | }; 13 | 14 | export default Header; 15 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/_components/intro.tsx: -------------------------------------------------------------------------------- 1 | import { CMS_NAME } from "@/lib/constants"; 2 | 3 | export function Intro() { 4 | return ( 5 | 6 | 7 | Blog. 8 | 9 | 10 | A statically generated blog example using{" "} 11 | 15 | Next.js 16 | {" "} 17 | and {CMS_NAME}. 18 | 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/_components/markdown-styles.module.css: -------------------------------------------------------------------------------- 1 | .markdown { 2 | @apply text-lg leading-relaxed; 3 | } 4 | 5 | .markdown p, 6 | .markdown ul, 7 | .markdown ol, 8 | .markdown blockquote { 9 | @apply my-6; 10 | } 11 | 12 | .markdown h2 { 13 | @apply text-3xl mt-12 mb-4 leading-snug; 14 | } 15 | 16 | .markdown h3 { 17 | @apply text-2xl mt-8 mb-4 leading-snug; 18 | } 19 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/_components/more-stories.tsx: -------------------------------------------------------------------------------- 1 | import { Post } from "@/interfaces/post"; 2 | import { PostPreview } from "./post-preview"; 3 | 4 | type Props = { 5 | posts: Post[]; 6 | }; 7 | 8 | export function MoreStories({ posts }: Props) { 9 | return ( 10 | 11 | 12 | More Stories 13 | 14 | 15 | {posts.map((post) => ( 16 | 25 | ))} 26 | 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/_components/post-body.tsx: -------------------------------------------------------------------------------- 1 | import markdownStyles from "./markdown-styles.module.css"; 2 | 3 | type Props = { 4 | content: string; 5 | }; 6 | 7 | export function PostBody({ content }: Props) { 8 | return ( 9 | 10 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/_components/post-header.tsx: -------------------------------------------------------------------------------- 1 | import Avatar from "./avatar"; 2 | import CoverImage from "./cover-image"; 3 | import DateFormatter from "./date-formatter"; 4 | import { PostTitle } from "@/app/_components/post-title"; 5 | import { type Author } from "@/interfaces/author"; 6 | 7 | type Props = { 8 | title: string; 9 | coverImage: string; 10 | date: string; 11 | author: Author; 12 | }; 13 | 14 | export function PostHeader({ title, coverImage, date, author }: Props) { 15 | return ( 16 | <> 17 | {title} 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | > 33 | ); 34 | } -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/_components/post-preview.tsx: -------------------------------------------------------------------------------- 1 | import { type Author } from "@/interfaces/author"; 2 | import Link from "next/link"; 3 | import Avatar from "./avatar"; 4 | import CoverImage from "./cover-image"; 5 | import DateFormatter from "./date-formatter"; 6 | 7 | type Props = { 8 | title: string; 9 | coverImage: string; 10 | date: string; 11 | excerpt: string; 12 | author: Author; 13 | slug: string; 14 | }; 15 | 16 | export function PostPreview({ 17 | title, 18 | coverImage, 19 | date, 20 | excerpt, 21 | author, 22 | slug, 23 | }: Props) { 24 | return ( 25 | 26 | 27 | 28 | 29 | 30 | 35 | {title} 36 | 37 | 38 | 39 | 40 | 41 | {excerpt} 42 | 43 | 44 | ); 45 | } -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/_components/post-title.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children?: ReactNode; 5 | }; 6 | 7 | export function PostTitle({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/_components/section-separator.tsx: -------------------------------------------------------------------------------- 1 | export function SectionSeparator() { 2 | return ; 3 | } 4 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import Container from "@/app/_components/container"; 2 | import { HeroPost } from "@/app/_components/hero-post"; 3 | import { Intro } from "@/app/_components/intro"; 4 | import { MoreStories } from "@/app/_components/more-stories"; 5 | import { getAllPosts } from "../lib/api"; 6 | 7 | export default async function Index() { 8 | 9 | const allPosts = await getAllPosts(); 10 | 11 | const heroPost = allPosts[0]; 12 | 13 | const morePosts = allPosts.slice(1); 14 | 15 | return ( 16 | 17 | 18 | 19 | 27 | {morePosts.length > 0 && } 28 | 29 | 30 | ); 31 | } -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/interfaces/author.ts: -------------------------------------------------------------------------------- 1 | export type Author = { 2 | name: string; 3 | picture: string; 4 | }; 5 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/interfaces/post.ts: -------------------------------------------------------------------------------- 1 | import { type Author } from "./author"; 2 | 3 | export type Post = { 4 | slug: string; 5 | title: string; 6 | date: string; 7 | coverImage: string; 8 | author: Author; 9 | excerpt: string; 10 | ogImage: { 11 | url: string; 12 | }; 13 | content: string; 14 | preview?: boolean; 15 | }; 16 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/lib/api.ts: -------------------------------------------------------------------------------- 1 | 'use server' 2 | import { Post } from "@/interfaces/post"; 3 | import fs from "fs"; 4 | import matter from "gray-matter"; 5 | import { join } from "path"; 6 | import { Client } from 'pg' 7 | import { getPostFromRow } from "./utils" 8 | 9 | const client = new Client({ 10 | ssl: { 11 | rejectUnauthorized: false, 12 | } 13 | }) 14 | 15 | const connect = async () => { 16 | await client.connect() 17 | } 18 | 19 | connect() 20 | 21 | export async function getPostBySlug(slug: string): Promise { 22 | const data = await client.query('SELECT * FROM posts WHERE slug=$1;',[slug]) 23 | const post = getPostFromRow(data.rows[0]) 24 | return post; 25 | } 26 | 27 | export async function getAllPosts(): Promise { 28 | const data = await client.query('SELECT * FROM posts;') 29 | const posts = data.rows.map((row) => (getPostFromRow(row))) 30 | return posts; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const EXAMPLE_PATH = "blog-starter"; 2 | export const CMS_NAME = "Markdown"; 3 | export const HOME_OG_IMAGE_URL = 4 | "https://og-image.vercel.app/Next.js%20Blog%20Starter%20Example.png?theme=light&md=1&fontSize=100px&images=https%3A%2F%2Fassets.vercel.com%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg"; 5 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/lib/markdownToHtml.ts: -------------------------------------------------------------------------------- 1 | import { remark } from "remark"; 2 | import html from "remark-html"; 3 | 4 | export default async function markdownToHtml(markdown: string) { 5 | const result = await remark().use(html).process(markdown); 6 | return result.toString(); 7 | } 8 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | 2 | export function getPostFromRow(row: any){ 3 | 4 | const post = { 5 | title: row.title, 6 | excerpt: row.excerpt, 7 | coverImage: row.coverimage, 8 | date: row.date.toISOString(), 9 | author: { 10 | name: row.author_name, 11 | picture: row.author_picture 12 | }, 13 | ogImage: { 14 | url: row.ogimage_url 15 | }, 16 | slug: row.slug, 17 | content: row.content, 18 | preview: false 19 | } 20 | return post; 21 | } 22 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/assets/blog/authors/jj.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/src/public/assets/blog/authors/jj.jpeg -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/assets/blog/authors/joe.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/src/public/assets/blog/authors/joe.jpeg -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/assets/blog/authors/tim.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/src/public/assets/blog/authors/tim.jpeg -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/assets/blog/dynamic-routing/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/src/public/assets/blog/dynamic-routing/cover.jpg -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/assets/blog/hello-world/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/src/public/assets/blog/hello-world/cover.jpg -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/assets/blog/preview/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/src/public/assets/blog/preview/cover.jpg -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/src/public/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/src/public/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/src/public/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #000000 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/src/public/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/src/public/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/src/public/favicon/favicon.ico -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scaleway/serverless-examples/e6d614327867bec0c009f09ca440ca6c7e586c68/projects/tutorial-sdb-nextjs-terraform/src/public/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/src/public/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Next.js", 3 | "short_name": "Next.js", 4 | "icons": [ 5 | { 6 | "src": "/favicons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/favicons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#000000", 17 | "background_color": "#000000", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 13 | "gradient-conic": 14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 15 | }, 16 | colors: { 17 | "accent-1": "#FAFAFA", 18 | "accent-2": "#EAEAEA", 19 | "accent-7": "#333", 20 | success: "#0070f3", 21 | cyan: "#79FFE1", 22 | }, 23 | spacing: { 24 | 28: "7rem", 25 | }, 26 | letterSpacing: { 27 | tighter: "-.04em", 28 | }, 29 | fontSize: { 30 | "5xl": "2.5rem", 31 | "6xl": "2.75rem", 32 | "7xl": "4.5rem", 33 | "8xl": "6.25rem", 34 | }, 35 | boxShadow: { 36 | sm: "0 5px 10px rgba(0, 0, 0, 0.12)", 37 | md: "0 8px 30px rgba(0, 0, 0, 0.12)", 38 | }, 39 | }, 40 | }, 41 | plugins: [], 42 | }; 43 | export default config; 44 | -------------------------------------------------------------------------------- /projects/tutorial-sdb-nextjs-terraform/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./src/*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | --------------------------------------------------------------------------------
{excerpt}