├── .env ├── .github ├── chainguard │ └── release.sts.yaml ├── dependabot.yml └── workflows │ ├── manage.yml │ └── shopware.yml ├── .gitignore ├── Dockerfile ├── README.md ├── data └── data │ ├── conf.d │ └── middlewares.yml │ └── traefik.yml ├── docker-compose.yml ├── examples └── create_instance.php ├── go.mod ├── go.sum ├── handler ├── create_env.go ├── delete_container.go ├── info.go ├── init.go ├── list_container.go └── update.go ├── images ├── 5 │ ├── Dockerfile │ └── entrypoint.sh └── 6 │ ├── Dockerfile │ ├── build.ts │ └── rootfs │ ├── entrypoint.sh │ ├── etc │ ├── nginx │ │ ├── cron.conf │ │ ├── nginx.conf │ │ └── sites-enabled │ │ │ └── shopware.conf │ └── supervisord.conf │ ├── usr │ └── local │ │ └── etc │ │ ├── php-fpm.d │ │ └── zz-docker.conf │ │ └── php │ │ └── conf.d │ │ └── docker.ini │ └── var │ └── www │ └── shop │ └── config │ └── packages │ ├── queue.yaml │ └── trusted_env.yaml ├── main.go └── version.txt /.env: -------------------------------------------------------------------------------- 1 | BASE_HOST=testenv.shopware.in 2 | -------------------------------------------------------------------------------- /.github/chainguard/release.sts.yaml: -------------------------------------------------------------------------------- 1 | issuer: https://token.actions.githubusercontent.com 2 | subject_pattern: repo:shopware/shopware:ref:refs/tags/v[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ 3 | claim_pattern: 4 | job_workflow_ref: shopware/shopware/.github/workflows/05-publish-release.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ 5 | 6 | permissions: 7 | actions: write 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/manage.yml: -------------------------------------------------------------------------------- 1 | name: Build Manager Image 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | paths: 7 | - go.mod 8 | - go.sum 9 | - handler/** 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Clone 17 | uses: actions/checkout@v3 18 | 19 | - name: Set up Docker Buildx 20 | uses: docker/setup-buildx-action@v2 21 | 22 | - name: Login into Github Docker Registery 23 | run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin 24 | 25 | - uses: docker/build-push-action@v4 26 | with: 27 | tags: ghcr.io/shopwarelabs/testenv/manage 28 | platforms: linux/amd64 29 | push: true 30 | provenance: false 31 | -------------------------------------------------------------------------------- /.github/workflows/shopware.yml: -------------------------------------------------------------------------------- 1 | name: Build Shopware 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | paths: 7 | - images/6/** 8 | 9 | jobs: 10 | build: 11 | runs-on: ${{ matrix.runner }} 12 | strategy: 13 | matrix: 14 | include: 15 | - runner: ubuntu-latest 16 | arch: amd64 17 | - runner: buildjet-2vcpu-ubuntu-2204-arm 18 | arch: arm64 19 | 20 | steps: 21 | - name: Clone 22 | uses: actions/checkout@v3 23 | 24 | - name: Set up Docker Buildx 25 | uses: docker/setup-buildx-action@v2 26 | 27 | - name: Login into Github Docker Registery 28 | run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin 29 | 30 | - name: Get latest version 31 | run: | 32 | VERSION=$(curl -s https://api.github.com/repos/shopware/shopware/tags | jq -r '.[0].name' | sed 's/^.//') 33 | TAG=$(echo $VERSION | tr . ' ' | awk '{print $1 "." $2 "." $3}') 34 | 35 | echo "version=$VERSION" >> $GITHUB_ENV 36 | echo "tag=$TAG" >> $GITHUB_ENV 37 | 38 | - uses: docker/build-push-action@v4 39 | with: 40 | tags: ghcr.io/shopwarelabs/testenv:${{ env.tag }}-${{ matrix.arch }} 41 | build-args: | 42 | SHOPWARE_VERSION=${{ env.version }} 43 | context: images/6 44 | cache-from: type=gha 45 | cache-to: type=gha,mode=max 46 | push: true 47 | provenance: false 48 | 49 | merge-manifest: 50 | name: Merge Manifest 51 | runs-on: ubuntu-latest 52 | needs: 53 | - build 54 | steps: 55 | - name: Login into Github Docker Registery 56 | run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin 57 | 58 | - name: Get latest version 59 | run: | 60 | VERSION=$(curl -s https://api.github.com/repos/shopware/shopware/tags | jq -r '.[0].name' | sed 's/^.//') 61 | TAG=$(echo $VERSION | tr . ' ' | awk '{print $1 "." $2 "." $3}') 62 | 63 | echo "version=$VERSION" >> $GITHUB_ENV 64 | echo "tag=$TAG" >> $GITHUB_ENV 65 | 66 | - run: docker manifest create ghcr.io/shopwarelabs/testenv:${{ env.tag }} --amend ghcr.io/shopwarelabs/testenv:${{ env.tag }}-amd64 --amend ghcr.io/shopwarelabs/testenv:${{ env.tag }}-arm64 67 | 68 | - run: docker manifest push ghcr.io/shopwarelabs/testenv:${{ env.tag }} 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vendor 3 | .env 4 | /plugin.json 5 | /test.php -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.20 as build-env 2 | 3 | WORKDIR /go/src/app 4 | ADD . /go/src/app 5 | 6 | RUN go get -d -v ./... 7 | 8 | RUN go build -o /go/bin/app 9 | 10 | FROM gcr.io/distroless/base 11 | COPY --from=build-env /go/bin/app / 12 | EXPOSE 8080 13 | CMD ["/app"] 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Environment for Testing Plugins 2 | 3 | This environment is in use internally for testing store plugins. 4 | 5 | Each created instance has an own subdomain. The Shopware installation runs in a subfolder `/shop/public`. 6 | The Adminer Plugin and App-System are preinstalled. 7 | 8 | **This Application has only an API** 9 | 10 | **This Application should run only in internal networks** 11 | 12 | ## Just running the Docker Container 13 | 14 | ```bash 15 | docker run --rm -p 80:80 -e VIRTUAL_HOST=localhost ghcr.io/shopwarelabs/testenv:6.4.3 16 | ``` 17 | 18 | Access shop at http://localhost/shop/public 19 | 20 | ### Admin Credentials 21 | 22 | User: `demo` 23 | Password: `demodemo` 24 | 25 | ## API 26 | 27 | ### GET /environments 28 | 29 | Returns all running containers 30 | 31 | 32 | ### POST /environments 33 | 34 | JSON Request: 35 | 36 | ```json 37 | { 38 | "installVersion": "", 39 | "plugin": "" 40 | } 41 | ``` 42 | 43 | Response 44 | 45 | ```json 46 | { 47 | "id": "", 48 | "domain": "", 49 | "installedVersion": "" 50 | } 51 | ``` 52 | 53 | ### DELETE /environments?id=dockerId 54 | 55 | Response 56 | 57 | ```json 58 | { 59 | "success": true 60 | } 61 | ``` 62 | -------------------------------------------------------------------------------- /data/data/conf.d/middlewares.yml: -------------------------------------------------------------------------------- 1 | middlewares: 2 | web-redirect: 3 | redirectScheme: 4 | scheme: https 5 | permanent: true 6 | compress: 7 | compress: {} -------------------------------------------------------------------------------- /data/data/traefik.yml: -------------------------------------------------------------------------------- 1 | api: 2 | dashboard: false 3 | 4 | pilot: 5 | dashboard: false 6 | 7 | entryPoints: 8 | web: 9 | address: ":80" 10 | websecure: 11 | address: ":443" 12 | 13 | providers: 14 | docker: 15 | endpoint: "unix:///var/run/docker.sock" 16 | exposedByDefault: false 17 | file: 18 | directory: /traefik.conf.d 19 | watch: true 20 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | traefik: 5 | image: traefik:2.4 6 | volumes: 7 | - /etc/localtime:/etc/localtime:ro 8 | - "/var/run/docker.sock:/var/run/docker.sock:ro" 9 | - ./data/traefik.yml:/traefik.yml:ro 10 | - ./data/conf.d:/traefik.conf.d/ 11 | ports: 12 | - "80:80" 13 | - "443:443" 14 | api: 15 | image: shopware/testenv-platform 16 | env_file: .env 17 | build: 18 | context: . 19 | dockerfile: Dockerfile 20 | volumes: 21 | - /var/run/docker.sock:/var/run/docker.sock:ro 22 | - /tmp:/tmp 23 | labels: 24 | - "traefik.enable=true" 25 | - "traefik.http.routers.api.rule=Host(`${BASE_HOST}`)" 26 | - "traefik.http.routers.api.entrypoints=web" 27 | - "traefik.http.services.api.loadbalancer.server.port=8080" 28 | -------------------------------------------------------------------------------- /examples/create_instance.php: -------------------------------------------------------------------------------- 1 | '6.4.3.1', 10 | 'plugin' => base64_encode(file_get_contents('https://github.com/FriendsOfShopware/FroshDevelopmentHelper/releases/download/0.3.2/FroshDevelopmentHelper.zip')) 11 | ])); 12 | curl_exec($ch); 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/shopwareLabs/testenv-platform 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect 7 | github.com/Microsoft/go-winio v0.5.2 // indirect 8 | github.com/docker/distribution v2.8.2+incompatible // indirect 9 | github.com/docker/docker v24.0.9+incompatible 10 | github.com/docker/go-connections v0.4.0 // indirect 11 | github.com/docker/go-units v0.4.0 // indirect 12 | github.com/gogo/protobuf v1.3.2 // indirect 13 | github.com/hashicorp/go-version v1.4.0 14 | github.com/julienschmidt/httprouter v1.3.0 15 | github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect 16 | github.com/morikuni/aec v1.0.0 // indirect 17 | github.com/opencontainers/go-digest v1.0.0 // indirect 18 | github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect 19 | github.com/stretchr/testify v1.7.0 // indirect 20 | golang.org/x/net v0.36.0 // indirect 21 | golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect 22 | gopkg.in/yaml.v3 v3.0.0 // indirect 23 | gotest.tools/v3 v3.0.3 // indirect 24 | ) 25 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= 2 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= 3 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= 4 | github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= 5 | github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= 6 | github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= 11 | github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 12 | github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= 13 | github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 14 | github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= 15 | github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= 16 | github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= 17 | github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 18 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 19 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 20 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 21 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 22 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 23 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 24 | github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4= 25 | github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 26 | github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= 27 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 28 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 29 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 30 | github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 h1:yH0SvLzcbZxcJXho2yh7CqdENGMQe73Cw3woZBpPli0= 31 | github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= 32 | github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 33 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 34 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 35 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 36 | github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= 37 | github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= 38 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 39 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 40 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 41 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 42 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 43 | github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= 44 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 45 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 46 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 47 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 48 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 49 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 50 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 51 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 52 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 53 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 54 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 55 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 56 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 57 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= 58 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 59 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 60 | golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= 61 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 62 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 63 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 64 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 65 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 66 | golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 67 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 68 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 69 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 70 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 71 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 72 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 73 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 74 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 75 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 76 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 77 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 78 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 79 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 80 | golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= 81 | golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= 82 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 83 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 84 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 85 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 86 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 87 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 88 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 89 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 90 | golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 91 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 92 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 93 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 94 | golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 95 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 96 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 97 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 98 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 99 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 100 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 101 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 102 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 103 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 104 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 105 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 106 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 107 | golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= 108 | golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 109 | golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 110 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 111 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 112 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 113 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 114 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= 115 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 116 | golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 117 | golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= 118 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 119 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 120 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 121 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 122 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 123 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 124 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 125 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 126 | golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= 127 | golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= 128 | golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 129 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 130 | golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 131 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 132 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 133 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 134 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 135 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 136 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 137 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 138 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 139 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 140 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 141 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 142 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 143 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 144 | gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= 145 | gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 146 | gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= 147 | gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= 148 | gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= 149 | -------------------------------------------------------------------------------- /handler/create_env.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "archive/zip" 5 | "encoding/base64" 6 | "encoding/json" 7 | "fmt" 8 | "github.com/docker/docker/api/types" 9 | "github.com/docker/docker/api/types/container" 10 | "github.com/docker/docker/api/types/filters" 11 | "github.com/docker/docker/api/types/network" 12 | "github.com/julienschmidt/httprouter" 13 | "io" 14 | "io/ioutil" 15 | "log" 16 | "net/http" 17 | "os" 18 | "path/filepath" 19 | "strings" 20 | ) 21 | 22 | func CreateEnvironment(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { 23 | id := randSeq(5) 24 | request, err := getPluginInformationFromRequest(id, r) 25 | scheme := "http" 26 | 27 | if os.Getenv("USE_HTTPS") == "1" { 28 | scheme = "https" 29 | } 30 | 31 | if err != nil { 32 | log.Printf("%s", err) 33 | apiResponse(w, ApiResponse{Success: false, Message: fmt.Sprintf("%s", err)}, http.StatusInternalServerError) 34 | return 35 | } 36 | 37 | log.Printf("Requested environment for version: %s and plugin: %s", request.InstallVersion, request.Name) 38 | 39 | instanceName := strings.ToLower(fmt.Sprintf("%s-%s", request.Name, id)) 40 | host := strings.ReplaceAll(strings.ToLower(fmt.Sprintf("%s.%s", instanceName, os.Getenv("BASE_HOST"))), "_", "-") 41 | 42 | imageName, err := getImage(request) 43 | if err != nil { 44 | log.Printf("%s", err) 45 | apiResponse(w, ApiResponse{Success: false, Message: fmt.Sprintf("%s", err)}, http.StatusInternalServerError) 46 | return 47 | } 48 | 49 | appUrl := fmt.Sprintf("%s://%s/shop/public", scheme, host) 50 | labels := map[string]string{ 51 | "testenv": "1", 52 | "traefik.enable": "true", 53 | fmt.Sprintf("traefik.http.routers.http-%s.rule", instanceName): fmt.Sprintf("Host(`%s`)", host), 54 | fmt.Sprintf("traefik.http.routers.http-%s.entrypoints", instanceName): "web", 55 | } 56 | 57 | if len(os.Getenv("SSL_PROVIDER")) > 0 { 58 | labels[fmt.Sprintf("traefik.http.routers.http-%s.middlewares", instanceName)] = "web-redirect@file" 59 | labels[fmt.Sprintf("traefik.http.routers.https-%s.middlewares", instanceName)] = "compress@file" 60 | labels[fmt.Sprintf("traefik.http.routers.https-%s.rule", instanceName)] = fmt.Sprintf("Host(`%s`)", host) 61 | labels[fmt.Sprintf("traefik.http.routers.https-%s.entrypoints", instanceName)] = "websecure" 62 | labels[fmt.Sprintf("traefik.http.routers.https-%s.tls", instanceName)] = "true" 63 | labels[fmt.Sprintf("traefik.http.routers.https-%s.tls.certresolver", instanceName)] = os.Getenv("SSL_PROVIDER") 64 | labels[fmt.Sprintf("traefik.http.routers.https-%s.tls.domains[0].main", instanceName)] = os.Getenv("BASE_HOST") 65 | labels[fmt.Sprintf("traefik.http.routers.https-%s.tls.domains[0].sans", instanceName)] = fmt.Sprintf("*.%s", os.Getenv("BASE_HOST")) 66 | } 67 | 68 | cConfig := &container.Config{ 69 | Image: imageName, 70 | Env: []string{ 71 | fmt.Sprintf("PLUGIN_NAME=%s", request.Name), 72 | fmt.Sprintf("VIRTUAL_HOST=%s", host), 73 | fmt.Sprintf("APP_URL=%s", appUrl), 74 | fmt.Sprintf("SHOPWARE_DEMO_USER_PASSWORD=%s", request.ShopwarePassword), 75 | }, 76 | Labels: labels, 77 | } 78 | 79 | cHost := &container.HostConfig{ 80 | Binds: []string{ 81 | fmt.Sprintf("%s:%s", request.VolumeFolder, fmt.Sprintf("/var/www/shop/%s", request.MountFolder)), 82 | }, 83 | } 84 | cNetwork := &network.NetworkingConfig{ 85 | EndpointsConfig: map[string]*network.EndpointSettings{ 86 | "docker_default": {}, 87 | }, 88 | } 89 | 90 | cBody, err := dClient.ContainerCreate(ctx, cConfig, cHost, cNetwork, nil, instanceName) 91 | 92 | if err != nil { 93 | log.Printf("%s", err) 94 | apiResponse(w, ApiResponse{Success: false, Message: fmt.Sprintf("%s", err)}, http.StatusInternalServerError) 95 | return 96 | } 97 | 98 | err = dClient.ContainerStart(ctx, cBody.ID, types.ContainerStartOptions{}) 99 | 100 | if err != nil { 101 | log.Printf("%s", err) 102 | apiResponse(w, ApiResponse{Success: false, Message: fmt.Sprintf("%s", err)}, http.StatusInternalServerError) 103 | return 104 | } 105 | 106 | apiResponse(w, EnvironmentCreated{ 107 | ID: cBody.ID, 108 | URL: appUrl, 109 | InstallVersion: request.InstallVersion, 110 | ShopwarePassword: request.ShopwarePassword, 111 | }, http.StatusOK) 112 | } 113 | 114 | func getPluginInformationFromRequest(id string, r *http.Request) (*PluginInformation, error) { 115 | body, err := ioutil.ReadAll(r.Body) 116 | 117 | if err != nil { 118 | return nil, err 119 | } 120 | 121 | var env EnvironmentRequest 122 | err = json.Unmarshal(body, &env) 123 | 124 | if err != nil { 125 | return nil, err 126 | } 127 | 128 | pluginZipContentByte, err := base64.StdEncoding.DecodeString(env.PluginZipEncoded) 129 | 130 | if err != nil { 131 | return nil, err 132 | } 133 | 134 | pluginZipContent := string(pluginZipContentByte) 135 | 136 | zipReader, err := zip.NewReader(strings.NewReader(pluginZipContent), int64(len(pluginZipContent))) 137 | 138 | if err != nil { 139 | return nil, err 140 | } 141 | 142 | result := PluginInformation{EnvironmentRequest: env} 143 | 144 | if len(zipReader.File) == 0 { 145 | return nil, fmt.Errorf("Zip is empty") 146 | } 147 | 148 | names := strings.Split(zipReader.File[0].Name, "/") 149 | 150 | result.Name = names[0] 151 | 152 | if env.InstallVersion == "app" { 153 | result.MountFolder = "custom/apps/" 154 | } else if result.Name == "Backend" || result.Name == "Core" || result.Name == "Frontend" { 155 | result.MountFolder = "engine/Shopware/Plugins/Local/" 156 | } else { 157 | result.MountFolder = "custom/plugins/" 158 | } 159 | 160 | result.VolumeFolder, err = ioutil.TempDir("", "plugin") 161 | if err != nil { 162 | return nil, err 163 | } 164 | 165 | err = Unzip(zipReader, result.VolumeFolder) 166 | 167 | err = os.Chown(result.VolumeFolder, 1000, 1000) 168 | if err != nil { 169 | return nil, err 170 | } 171 | 172 | if LookupEnvOrString("GENERATE_INSTANCE_PASSWORDS", "0") == "1" { 173 | result.ShopwarePassword = randSeq(10) 174 | } else { 175 | result.ShopwarePassword = "demo" 176 | } 177 | 178 | return &result, nil 179 | } 180 | 181 | func Unzip(r *zip.Reader, dest string) error { 182 | for _, f := range r.File { 183 | 184 | // Store filename/path for returning and using later on 185 | fpath := filepath.Join(dest, f.Name) 186 | 187 | // Check for ZipSlip. More Info: http://bit.ly/2MsjAWE 188 | if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) { 189 | return fmt.Errorf("%s: illegal file path", fpath) 190 | } 191 | 192 | if f.FileInfo().IsDir() { 193 | // Make Folder 194 | os.MkdirAll(fpath, os.ModePerm) 195 | continue 196 | } 197 | 198 | // Make File 199 | if err := os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { 200 | return err 201 | } 202 | 203 | outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) 204 | if err != nil { 205 | return err 206 | } 207 | 208 | rc, err := f.Open() 209 | if err != nil { 210 | return err 211 | } 212 | 213 | _, err = io.Copy(outFile, rc) 214 | 215 | // Close the file without defer to close before next iteration of loop 216 | outFile.Close() 217 | rc.Close() 218 | 219 | if err != nil { 220 | return err 221 | } 222 | 223 | err = os.Chown(fpath, 1000, 1000) 224 | if err != nil { 225 | return err 226 | } 227 | } 228 | 229 | return nil 230 | } 231 | 232 | func getImage(info *PluginInformation) (string, error) { 233 | if info.InstallVersion == "app" { 234 | return getNewestShopwareImage(), nil 235 | } 236 | 237 | var v1, v2, v3 int 238 | var imageTag string 239 | 240 | _, err := fmt.Sscanf(info.InstallVersion, "%d.%d.%d", &v1, &v2, &v3) 241 | if err != nil { 242 | return "", err 243 | } 244 | 245 | if v2 >= 3 { 246 | imageTag = fmt.Sprintf("%d.%d.%d", v1, v2, v3) 247 | } else { 248 | imageTag = fmt.Sprintf("%d.%d", v1, v2) 249 | } 250 | 251 | imageName := fmt.Sprintf("ghcr.io/shopwarelabs/testenv:%s", imageTag) 252 | opts := types.ImageListOptions{} 253 | opts.Filters = filters.NewArgs() 254 | opts.Filters.Add("before", imageName) 255 | 256 | _, err = dClient.ImageList(ctx, opts) 257 | if err != nil { 258 | return "", err 259 | } 260 | 261 | return imageName, nil 262 | } 263 | 264 | type EnvironmentRequest struct { 265 | InstallVersion string `json:"installVersion"` 266 | PluginZipEncoded string `json:"plugin"` 267 | ShopwarePassword string 268 | } 269 | 270 | type PluginInformation struct { 271 | Name string 272 | VolumeFolder string 273 | MountFolder string 274 | EnvironmentRequest 275 | } 276 | 277 | type EnvironmentCreated struct { 278 | ID string `json:"id"` 279 | URL string `json:"domain"` 280 | InstallVersion string `json:"installVersion"` 281 | ShopwarePassword string `json:"shopwarePassword"` 282 | } 283 | -------------------------------------------------------------------------------- /handler/delete_container.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/docker/docker/api/types" 7 | "github.com/julienschmidt/httprouter" 8 | "log" 9 | "net/http" 10 | "os" 11 | "strings" 12 | ) 13 | 14 | func DeleteContainer(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { 15 | keys, ok := r.URL.Query()["id"] 16 | 17 | defer deleteResponse(w) 18 | 19 | if !ok || len(keys[0]) < 1 { 20 | fmt.Fprintln(w, "Url Param 'id' is missing") 21 | return 22 | } 23 | 24 | id := string(keys[0]) 25 | log.Printf("Requested deletion of container: %s", id) 26 | 27 | container, err := dClient.ContainerInspect(ctx, id) 28 | 29 | if err != nil { 30 | log.Println(err) 31 | return 32 | } 33 | 34 | err = dClient.ContainerKill(ctx, id, "SIGKILL") 35 | 36 | if err != nil { 37 | log.Println(err) 38 | return 39 | } 40 | 41 | err = dClient.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true}) 42 | 43 | if err != nil { 44 | log.Println(err) 45 | return 46 | } 47 | 48 | for _, bind := range container.HostConfig.Binds { 49 | _ = os.RemoveAll(strings.Split(bind, ":")[0]) 50 | } 51 | } 52 | 53 | func deleteResponse(w http.ResponseWriter) { 54 | res, _ := json.Marshal(map[string]bool{"success": true}) 55 | 56 | w.Header().Set("Content-Type", "application/json") 57 | _, _ = w.Write(res) 58 | } 59 | -------------------------------------------------------------------------------- /handler/info.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/julienschmidt/httprouter" 5 | "net/http" 6 | ) 7 | 8 | func Info(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) { 9 | w.Header().Set("Content-Type", "application/json") 10 | _, _ = w.Write([]byte("{\"message\": \"API server up and running\"}")) 11 | } 12 | -------------------------------------------------------------------------------- /handler/init.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "github.com/docker/docker/client" 7 | "math/rand" 8 | "net/http" 9 | "os" 10 | ) 11 | 12 | var ctx context.Context 13 | var dClient *client.Client 14 | 15 | func init() { 16 | ctx = context.Background() 17 | 18 | var err error 19 | 20 | dClient, err = client.NewEnvClient() 21 | if err != nil { 22 | panic(err) 23 | } 24 | } 25 | 26 | var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 27 | 28 | func randSeq(n int) string { 29 | b := make([]rune, n) 30 | for i := range b { 31 | b[i] = letters[rand.Intn(len(letters))] 32 | } 33 | return string(b) 34 | } 35 | 36 | type ApiResponse struct { 37 | Success bool `json:"success"` 38 | Message string `json:"message"` 39 | } 40 | 41 | func apiResponse(w http.ResponseWriter, aResp interface{}, statusCode int) { 42 | res, _ := json.Marshal(aResp) 43 | 44 | w.Header().Set("Content-Type", "application/json") 45 | w.WriteHeader(statusCode) 46 | _, _ = w.Write(res) 47 | } 48 | 49 | func LookupEnvOrString(key string, defaultVal string) string { 50 | if val, ok := os.LookupEnv(key); ok { 51 | return val 52 | } 53 | return defaultVal 54 | } 55 | -------------------------------------------------------------------------------- /handler/list_container.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/docker/docker/api/types" 7 | "github.com/docker/docker/api/types/filters" 8 | "github.com/julienschmidt/httprouter" 9 | "log" 10 | "net/http" 11 | ) 12 | 13 | func ListContainer(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) { 14 | opts := types.ContainerListOptions{} 15 | opts.Filters = filters.NewArgs() 16 | opts.Filters.Add("label", "testenv=1") 17 | 18 | containers, err := dClient.ContainerList(ctx, opts) 19 | 20 | if err != nil { 21 | log.Println(err) 22 | w.WriteHeader(http.StatusInternalServerError) 23 | fmt.Fprint(w, "Cannot list containers") 24 | } 25 | 26 | jData, _ := json.Marshal(containers) 27 | 28 | w.Header().Set("Content-Type", "application/json") 29 | _, _ = w.Write(jData) 30 | } 31 | -------------------------------------------------------------------------------- /handler/update.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/docker/docker/api/types/filters" 7 | "github.com/hashicorp/go-version" 8 | "io/ioutil" 9 | "log" 10 | "sort" 11 | "strings" 12 | "time" 13 | 14 | "github.com/docker/docker/api/types" 15 | ) 16 | 17 | var newestShopwareVersion = "" 18 | 19 | func getNewestShopwareImage() string { 20 | if len(newestShopwareVersion) == 0 { 21 | return "ghcr.io/shopwarelabs/testenv:6.3.4" 22 | } 23 | 24 | return newestShopwareVersion 25 | } 26 | 27 | func PullImageUpdatesTask() { 28 | for { 29 | PullImageUpdates() 30 | time.Sleep(time.Hour * 24) 31 | } 32 | } 33 | 34 | func PullImageUpdates() { 35 | log.Println("Pulling images") 36 | outputReader, err := dClient.ImagePull(context.Background(), "ghcr.io/shopwarelabs/testenv", types.ImagePullOptions{All: true}) 37 | 38 | if err != nil { 39 | log.Println(err) 40 | return 41 | } 42 | 43 | text, _ := ioutil.ReadAll(outputReader) 44 | fmt.Println(string(text)) 45 | 46 | log.Println("Pulled new images") 47 | log.Println("Detecting newest version") 48 | 49 | opts := types.ImageListOptions{} 50 | 51 | opts.Filters = filters.NewArgs() 52 | opts.Filters.Add("reference", "ghcr.io/shopwarelabs/testenv") 53 | 54 | images, err := dClient.ImageList(context.Background(), opts) 55 | 56 | if err != nil { 57 | log.Println(err) 58 | return 59 | } 60 | 61 | versions := make([]*version.Version, 0) 62 | 63 | for _, image := range images { 64 | if len(image.RepoTags) == 0 { 65 | continue 66 | } 67 | 68 | v, err := version.NewVersion(strings.Replace(image.RepoTags[0], "ghcr.io/shopwarelabs/testenv:", "", 1)) 69 | 70 | if err != nil { 71 | log.Println(err) 72 | continue 73 | } 74 | 75 | versions = append(versions, v) 76 | } 77 | 78 | sort.Sort(version.Collection(versions)) 79 | newestShopwareVersion = fmt.Sprintf("ghcr.io/shopwarelabs/testenv:%s", versions[len(versions)-1].String()) 80 | 81 | log.Println("Completed update task") 82 | log.Printf("New newest Shopware version is: %s\n", newestShopwareVersion) 83 | } 84 | 85 | type GithubApiResponse []struct { 86 | ID int `json:"id"` 87 | Name string `json:"name"` 88 | URL string `json:"url"` 89 | PackageHTMLURL string `json:"package_html_url"` 90 | CreatedAt time.Time `json:"created_at"` 91 | UpdatedAt time.Time `json:"updated_at"` 92 | HTMLURL string `json:"html_url"` 93 | Metadata struct { 94 | PackageType string `json:"package_type"` 95 | Container struct { 96 | Tags []string `json:"tags"` 97 | } `json:"container"` 98 | } `json:"metadata"` 99 | } 100 | -------------------------------------------------------------------------------- /images/5/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/shyim/shopware-docker/5/nginx-production:php74 2 | 3 | COPY --from=composer /usr/bin/composer /usr/bin/composer 4 | 5 | ARG SHOPWARE_DL=https://www.shopware.com/de/Download/redirect/version/sw5/file/install_5.6.8_7b49bfb8ea0d5269b349722157fe324a341ed28e.zip 6 | 7 | RUN apk add --no-cache mysql \ 8 | mysql-client \ 9 | sudo \ 10 | git && \ 11 | cd /var/www/html && \ 12 | wget $SHOPWARE_DL && \ 13 | unzip *.zip && \ 14 | rm *.zip && \ 15 | mysql_install_db --datadir=/var/lib/mysql --user=mysql 16 | 17 | RUN mkdir /run/mysqld/ && chown -R mysql:mysql /run/mysqld/ && /usr/bin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql & sleep 2 && \ 18 | mysql -e "CREATE DATABASE shopware" && \ 19 | mysqladmin --user=root password 'root' && \ 20 | php /var/www/html/recovery/install/index.php --shop-host localhost --db-host localhost --db-socket /run/mysqld/mysqld.sock --db-user root --db-password root --db-name shopware --shop-locale en_GB --shop-currency EUR --admin-username demo --admin-password demo --admin-email demo@foo.com --admin-locale en_GB --admin-name demo -n && \ 21 | php /var/www/html/bin/console sw:firstrunwizard:disable && \ 22 | php /var/www/html/bin/console sw:store:download SwagDemoDataEN && \ 23 | php /var/www/html/bin/console sw:plugin:install --activate SwagDemoDataEN && \ 24 | chown -R 1000:1000 /var/www/html 25 | 26 | COPY entrypoint.sh /entrypoint.sh 27 | 28 | CMD ["/bin/sh", "/entrypoint.sh"] 29 | -------------------------------------------------------------------------------- /images/5/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | /usr/bin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql & 4 | 5 | while ! mysqladmin ping --silent; do 6 | sleep 1 7 | done 8 | 9 | mysql shopware -e "UPDATE s_core_shops set host = '${VIRTUAL_HOST}'" 10 | 11 | sudo -E -u www-data git clone https://github.com/FriendsOfShopware/FroshAdminer.git /var/www/html/custom/plugins/FroshAdminer 12 | sudo -E -u www-data /var/www/html/bin/console sw:plugin:refresh 13 | 14 | sudo -E -u www-data /var/www/html/bin/console sw:plugin:install FroshAdminer --activate 15 | 16 | if [[ ! -z $PLUGIN_NAME ]]; then 17 | sudo -E -u www-data /var/www/html/bin/console sw:plugin:install $PLUGIN_NAME --activate 18 | fi 19 | 20 | rm -rf /var/www/html/var/cache/* 21 | 22 | /usr/bin/supervisord -c /etc/supervisord.conf 23 | -------------------------------------------------------------------------------- /images/6/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-fpm-alpine 2 | 3 | ENV TZ=Europe/Berlin \ 4 | APP_URL=http://localhost/shop/public \ 5 | APP_ENV=prod \ 6 | APP_SECRET=440dec3766de53010c5ccf6231c182acfc90bd25cff82e771245f736fd276518 \ 7 | INSTANCE_ID=10612e3916e153dd3447850e944a03fabe89440970295447a30a75b151bd844e \ 8 | DATABASE_URL=mysql://root:root@localhost/shopware \ 9 | MAILER_URL=smtp://mail:1025 \ 10 | SHOPWARE_ES_HOSTS=es \ 11 | SHOPWARE_ES_ENABLED=0 \ 12 | SHOPWARE_ES_INDEXING_ENABLED=0 \ 13 | SHOPWARE_ES_INDEX_PREFIX=shop \ 14 | COMPOSER_HOME=/tmp/composer \ 15 | SHOPWARE_HTTP_CACHE_ENABLED=1 \ 16 | SHOPWARE_HTTP_DEFAULT_TTL=7200 \ 17 | BLUE_GREEN_DEPLOYMENT=0 \ 18 | INSTALL_LOCALE=nl-NL \ 19 | INSTALL_CURRENCY=EUR \ 20 | FPM_PM=dynamic \ 21 | FPM_PM_MAX_CHILDREN=5 \ 22 | FPM_PM_START_SERVERS=2 \ 23 | FPM_PM_MIN_SPARE_SERVERS=1 \ 24 | FPM_PM_MAX_SPARE_SERVERS=3 \ 25 | PHP_MAX_UPLOAD_SIZE=128m \ 26 | PHP_MAX_EXECUTION_TIME=300 \ 27 | PHP_MEMORY_LIMIT=512m \ 28 | IPE_GD_WITHOUTAVIF=1 \ 29 | LD_PRELOAD="/usr/lib/preloadable_libiconv.so php" 30 | 31 | COPY --from=ochinchina/supervisord:latest /usr/local/bin/supervisord /usr/bin/supervisord 32 | COPY --from=composer/composer:2-bin /composer /usr/local/bin/composer 33 | COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/ 34 | COPY --from=ghcr.io/shyim/gnu-libiconv:v3.14 /gnu-libiconv-1.15-r3.apk /gnu-libiconv-1.15-r3.apk 35 | COPY --from=ghcr.io/friendsofshopware/shopware-cli /usr/local/bin/shopware-cli /usr/local/bin/shopware-cli 36 | 37 | RUN apk add --no-cache \ 38 | nginx \ 39 | shadow \ 40 | unzip \ 41 | wget \ 42 | sudo \ 43 | bash \ 44 | patch \ 45 | jq \ 46 | mariadb \ 47 | mariadb-client \ 48 | git && \ 49 | apk add --no-cache --allow-untrusted /gnu-libiconv-1.15-r3.apk && rm /gnu-libiconv-1.15-r3.apk && \ 50 | install-php-extensions bcmath gd intl mysqli pdo_mysql sockets bz2 soap zip gmp ffi redis opcache calendar pcntl && \ 51 | ln -s /usr/local/bin/php /usr/bin/php && \ 52 | ln -sf /dev/stdout /var/log/nginx/access.log && \ 53 | ln -sf /dev/stderr /var/log/nginx/error.log && \ 54 | rm -rf /var/lib/nginx/tmp && \ 55 | ln -sf /tmp /var/lib/nginx/tmp && \ 56 | mkdir -p /var/tmp/nginx/ && \ 57 | chown -R www-data:www-data /var/lib/nginx /var/tmp/nginx/ && \ 58 | chmod 777 -R /var/tmp/nginx/ && \ 59 | rm -rf /tmp/* && \ 60 | mkdir -p /var/www/ && \ 61 | chown -R www-data:www-data /var/www && \ 62 | usermod -u 1000 www-data && \ 63 | mysql_install_db --datadir=/var/lib/mysql --user=mysql && \ 64 | mkdir /run/mysqld/ && chown -R mysql:mysql /run/mysqld/ && \ 65 | apk add --no-cache icu-data-full 66 | 67 | ARG SHOPWARE_VERSION=6.5.5.1 68 | 69 | COPY rootfs/usr /usr 70 | 71 | RUN /usr/bin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql & sleep 2 && \ 72 | mysqladmin --user=root password 'root' && \ 73 | cd /var/www/ && \ 74 | COMPOSER_ALLOW_SUPERUSER=1 shopware-cli project create shop $SHOPWARE_VERSION && \ 75 | cd shop && \ 76 | php bin/console system:install --create-database --force --shop-locale nl-NL && \ 77 | php bin/console system:generate-jwt-secret || true && \ 78 | php bin/console user:create "demo" --admin --password="demodemo" -n && \ 79 | php bin/console sales-channel:create:storefront --name=Storefront --url="http://localhost/shop/public" && \ 80 | php bin/console theme:change --all Storefront && \ 81 | composer req --dev shopware/dev-tools --no-scripts && \ 82 | php -dmemory_limit=2G bin/console -e prod framework:demodata --products 3000 && \ 83 | php bin/console -e prod dal:refresh:index && \ 84 | mysql shopware -e "INSERT INTO system_config (id, configuration_key, configuration_value, sales_channel_id, created_at, updated_at) VALUES (X'b3ae4d7111114377af9480c4a0911111', 'core.frw.completedAt', '{\"_value\": \"2019-10-07T10:46:23+00:00\"}', NULL, '2019-10-07 10:46:23.169', NULL);" && \ 85 | php bin/console system:config:set core.metrics.shareUsageData false --json && \ 86 | composer req "frosh/tools:*" "frosh/adminer-platform:*" --no-scripts && \ 87 | composer remove --dev shopware/dev-tools --no-scripts && \ 88 | composer install --no-dev --no-scripts && \ 89 | php bin/console store:download -p SwagLanguagePack && \ 90 | git clone https://github.com/shopwareLabs/SwagTestEnvironment.git /var/www/shop/custom/plugins/SwagTestEnvironment --depth=1 && \ 91 | php bin/console plugin:refresh && \ 92 | php bin/console plugin:install -n --activate SwagLanguagePack FroshPlatformAdminer SwagTestEnvironment FroshTools 93 | 94 | COPY rootfs / 95 | 96 | RUN chown -R 1000 /var/www/shop /tmp/composer 97 | 98 | EXPOSE 80 99 | WORKDIR /var/www/shop 100 | 101 | STOPSIGNAL SIGKILL 102 | 103 | ENTRYPOINT ["/entrypoint.sh"] 104 | 105 | HEALTHCHECK --timeout=10s CMD curl --silent --fail http://127.0.0.1:80/shop/public/admin 106 | -------------------------------------------------------------------------------- /images/6/build.ts: -------------------------------------------------------------------------------- 1 | import * as semver from "https://deno.land/x/semver/mod.ts"; 2 | 3 | async function main() { 4 | let releases = await getReleases(); 5 | 6 | const ghConfig = { 7 | 'fail-fast': false, 8 | matrix: { 9 | include: [] as any 10 | } 11 | }; 12 | 13 | // Build 14 | for (let release of releases) { 15 | for (let tag of release.tags) { 16 | for (let imageName of release.imageNames) { 17 | ghConfig.matrix.include.push({ 18 | name: `Shopware ${tag}`, 19 | runs: { 20 | build: `cd images/6; docker buildx build --platform linux/amd64 --build-arg SHOPWARE_DL=${release.download} --build-arg SHOPWARE_VERSION=${release.version} --tag ${imageName}:${tag} --push .` 21 | } 22 | }); 23 | } 24 | } 25 | } 26 | 27 | await Deno.stdout.write(new TextEncoder().encode(JSON.stringify(ghConfig))); 28 | } 29 | 30 | function getMajorVersion(version: string) { 31 | let majorVersion = /\d+\.\d+.\d+/gm.exec(version); 32 | 33 | if (majorVersion && majorVersion[0]) { 34 | return majorVersion[0]; 35 | } 36 | 37 | return ''; 38 | } 39 | 40 | main(); 41 | 42 | async function getReleases() { 43 | let json = await (await fetch('https://n0g72msg55.execute-api.eu-central-1.amazonaws.com')).json(); 44 | let releases = []; 45 | let givenTags: string[] = []; 46 | 47 | 48 | for (let release of json) { 49 | const majorVersion = getMajorVersion(release.version); 50 | 51 | try { 52 | if (semver.lt(majorVersion, '6.4.0')) { 53 | continue; 54 | } 55 | } catch (e) { 56 | } 57 | 58 | if (!givenTags.includes(majorVersion)) { 59 | release.version = majorVersion; 60 | givenTags.push(majorVersion); 61 | } else { 62 | continue; 63 | } 64 | 65 | let image = { 66 | imageNames: ['ghcr.io/shopwarelabs/testenv'], 67 | version: release.version, 68 | download: release.download, 69 | tags: [release.version] 70 | } 71 | 72 | releases.push(image); 73 | } 74 | 75 | return releases; 76 | } 77 | -------------------------------------------------------------------------------- /images/6/rootfs/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | /usr/bin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql & 4 | 5 | unset APP_ENV 6 | echo "APP_ENV=prod" > /var/www/shop/.env.local 7 | chown -R 1000 /var/www/shop/.env 8 | 9 | while ! mysqladmin ping --silent; do 10 | sleep 1 11 | done 12 | 13 | if [[ -n $APP_URL ]]; then 14 | mysql -proot shopware -e "UPDATE sales_channel_domain set url = '${APP_URL}' where url = 'http://localhost/shop/public'" 15 | else 16 | mysql -proot shopware -e "UPDATE sales_channel_domain set url = 'http://${VIRTUAL_HOST}/shop/public' where url = 'http://localhost/shop/public'" 17 | fi 18 | 19 | rm -rf /var/www/shop/var/cache/* || true 20 | 21 | if [[ -n $SHOPWARE_DEMO_USER_PASSWORD ]]; then 22 | sudo -E -u www-data /var/www/shop/bin/console user:change-password demo -n --password="$SHOPWARE_DEMO_USER_PASSWORD" 23 | fi 24 | 25 | /usr/bin/supervisord -c /etc/supervisord.conf 26 | -------------------------------------------------------------------------------- /images/6/rootfs/etc/nginx/cron.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes auto; 3 | 4 | pid /tmp/nginx.pid; 5 | 6 | events { 7 | worker_connections 1024; 8 | } 9 | 10 | http { 11 | include mime.types; 12 | default_type application/octet-stream; 13 | sendfile on; 14 | keepalive_timeout 65; 15 | fastcgi_read_timeout 600; 16 | 17 | # Set https to 'on' if x-forwarded-proto is https 18 | server { 19 | listen 80; 20 | 21 | return 200 "OK"; 22 | } 23 | } -------------------------------------------------------------------------------- /images/6/rootfs/etc/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | daemon off; 3 | worker_processes auto; 4 | 5 | pid /tmp/nginx.pid; 6 | 7 | error_log /dev/stdout info; 8 | 9 | events { 10 | worker_connections 1024; 11 | } 12 | 13 | http { 14 | include mime.types; 15 | default_type application/octet-stream; 16 | sendfile on; 17 | keepalive_timeout 65; 18 | gzip on; 19 | fastcgi_read_timeout 600; 20 | server_tokens off; 21 | 22 | # Set https to 'on' if x-forwarded-proto is https 23 | map $http_x_forwarded_proto $fcgi_https { 24 | default off; 25 | https on; 26 | } 27 | 28 | 29 | include sites-enabled/*; 30 | } -------------------------------------------------------------------------------- /images/6/rootfs/etc/nginx/sites-enabled/shopware.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | 4 | index index.php index.html; 5 | server_name localhost; 6 | 7 | client_max_body_size 128M; 8 | 9 | root /var/www/; 10 | 11 | location / { 12 | return 302 /shop/public/; 13 | } 14 | 15 | # Shopware install / update 16 | location /shop/public/recovery/install { 17 | index index.php; 18 | try_files $uri /shop/public/recovery/install/index.php$is_args$args; 19 | } 20 | 21 | location /shop/public/recovery/update/ { 22 | location /shop/public/recovery/update/assets { 23 | } 24 | if (!-e $request_filename){ 25 | rewrite . /shop/public/recovery/update/index.php last; 26 | } 27 | } 28 | 29 | location /shop/public/ { 30 | try_files $uri /shop/public/index.php$is_args$args; 31 | } 32 | 33 | location ~ \.php$ { 34 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 35 | include fastcgi.conf; 36 | fastcgi_param HTTP_PROXY ""; 37 | fastcgi_param HTTPS $fcgi_https; 38 | fastcgi_buffers 8 16k; 39 | fastcgi_buffer_size 32k; 40 | fastcgi_read_timeout 300s; 41 | client_body_buffer_size 128k; 42 | fastcgi_pass 127.0.0.1:9000; 43 | http2_push_preload on; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /images/6/rootfs/etc/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | logfile=/dev/stderr 4 | logfile_maxbytes=0 5 | pidfile=/tmp/supervisord.pid 6 | 7 | [program:php-fpm] 8 | command=/usr/local/sbin/php-fpm 9 | redirect_stderr=true 10 | stdout_logfile=/dev/stderr 11 | stdout_logfile_maxbytes=0 12 | 13 | [program:nginx] 14 | command=/usr/sbin/nginx 15 | redirect_stderr=true 16 | stdout_logfile=/dev/stderr 17 | stdout_logfile_maxbytes=0 18 | 19 | [program:worker] 20 | command=/usr/local/bin/php /var/www/shop/bin/console messenger:consume async failed --memory-limit=256M --time-limit=60 21 | user=www-data 22 | redirect_stderr=true 23 | autorestart=true 24 | 25 | [program:scheduled-task] 26 | command=/usr/local/bin/php /var/www/shop/bin/console scheduled-task:run --memory-limit=256M --time-limit=60 27 | user=www-data 28 | redirect_stderr=true 29 | autorestart=true -------------------------------------------------------------------------------- /images/6/rootfs/usr/local/etc/php-fpm.d/zz-docker.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | daemonize = no 3 | 4 | [www] 5 | listen = 9000 6 | pm = ${FPM_PM} 7 | pm.max_children = ${FPM_PM_MAX_CHILDREN} 8 | pm.start_servers = ${FPM_PM_START_SERVERS} 9 | pm.min_spare_servers = ${FPM_PM_MIN_SPARE_SERVERS} 10 | pm.max_spare_servers = ${FPM_PM_MAX_SPARE_SERVERS} -------------------------------------------------------------------------------- /images/6/rootfs/usr/local/etc/php/conf.d/docker.ini: -------------------------------------------------------------------------------- 1 | upload_max_filesize = ${PHP_MAX_UPLOAD_SIZE} 2 | post_max_size = ${PHP_MAX_UPLOAD_SIZE} 3 | max_execution_time = ${PHP_MAX_EXECUTION_TIME} 4 | memory_limit = ${PHP_MEMORY_LIMIT} 5 | 6 | date.timezone = ${TZ} 7 | pdo_mysql.default_socket=/run/mysqld/mysqld.sock 8 | mysqli.default_socket=/run/mysqld/mysqld.sock 9 | memory_limit=1G -------------------------------------------------------------------------------- /images/6/rootfs/var/www/shop/config/packages/queue.yaml: -------------------------------------------------------------------------------- 1 | shopware: 2 | admin_worker: 3 | enable_admin_worker: false -------------------------------------------------------------------------------- /images/6/rootfs/var/www/shop/config/packages/trusted_env.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | trusted_proxies: 'REMOTE_ADDR' 3 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/julienschmidt/httprouter" 5 | "github.com/shopwareLabs/testenv-platform/handler" 6 | "log" 7 | "net/http" 8 | ) 9 | 10 | func main() { 11 | go handler.PullImageUpdatesTask() 12 | 13 | router := httprouter.New() 14 | 15 | router.GET("/", handler.Info) 16 | 17 | // New Routes 18 | router.GET("/environments", handler.ListContainer) 19 | router.POST("/environments", handler.CreateEnvironment) 20 | router.DELETE("/environments", handler.DeleteContainer) 21 | 22 | log.Println("Go!") 23 | log.Fatal(http.ListenAndServe("0.0.0.0:8080", router)) 24 | } 25 | -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | v6.4.17.2 --------------------------------------------------------------------------------