Skip to content

Commit

Permalink
Bugfix make podman work on fedora, apple silicone and windows OS (#74)
Browse files Browse the repository at this point in the history
* fix podman compose script with podman machine running with applehv due to bugs with qemu and m3 mac

* update all volume definitions so they match

* remove bind.selinux as it does not seem to have any affect

* Add updated readme for podman and add a hack for resolving the bind issues on mac

* update readme

* adde one line to readme.md

* test new notation for information and warnings

* test more with info sections

* try to fix indend

* Restructure the readme

* update readme

* some small changes to the README.md

* use host.docker.internal as it seems to work on both windows and mac

* change nginx internal domain to host.containers.internal

* Differentiate on host and container domains for podman

* bind problems also seems to be present on fedora. Renaming hack method in Makefile

* update readme with known issue about bind mounts

* fix bug in makefile

* Make local frontend port configurable

* Podman defaults localtest to 8000
Detect running local dev servers of app-frontend-react instead of static value

* Update readme with guide to add hyper-v firewall rule if needed

* big Z instead of small z

* Update known issue in readme

---------

Co-authored-by: tjololo <[email protected]>
  • Loading branch information
tjololo and tjololo authored Feb 5, 2024
1 parent c55ae94 commit 1d23de2
Show file tree
Hide file tree
Showing 16 changed files with 244 additions and 92 deletions.
3 changes: 2 additions & 1 deletion .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

## What port should the loadbalancer use?
## sometimes PCs have another service running on port 80, so you might need to
## change this. Default in podman-compose.yml is 8080 as ports below 1024 are privileged ports on most unix systems.
## change this.
## Default in podman-compose.yml is 8000 as ports below 1024 are privileged ports on most unix systems.

#ALTINN3LOCAL_PORT=80

Expand Down
18 changes: 17 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,20 @@ podman-start-localtest:

.PHONY: podman-stop-localtest
podman-stop-localtest:
podman compose --file podman-compose.yml down
podman compose --file podman-compose.yml down

.PHONY: podman-compose-start-localtest
podman-compose-start-localtest:
podman-compose --file podman-compose.yml up -d --build

.PHONY: podman-compose-stop-localtest
podman-compose-stop-localtest:
podman-compose --file podman-compose.yml down


.PHONY: podman-selinux-bind-hack
podman-selinux-bind-hack:
@echo "Running best effort commands to make bind mounts work on Apple Silicon and Linux with podman. Dirty hack until actual issue is located and fixed."
podman container run -v ./testdata/:/testdata/:Z --rm -it --entrypoint cat nginx:alpine-perl /testdata/authorization/claims/1337.json > /dev/null
podman container run -v ./loadbalancer/templates/:/testdata/:Z --rm -it --entrypoint cat nginx:alpine-perl /testdata/nginx.conf.conf > /dev/null
podman container run -v ./loadbalancer/www/:/testdata/:Z --rm -it --entrypoint cat nginx:alpine-perl /testdata/502App.html > /dev/null
172 changes: 105 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ These are some of the required steps, tips, and tricks when it comes to running

- [Prerequisites](#prerequisites)
- [Setup](#setup)
- [Clone the repository](#clone-the-repository)
- [Option A: Start the containers using podman](#option-a-start-the-containers-using-podman)
- [Option B: Start the containers using Docker](#option-b-start-the-containers-using-docker)
- [Start your app](#start-your-app)
- [Changing configuration](#changing-configuration)
- [Multiple apps at the same time (running LocalTest locally)](#multiple-apps-at-the-same-time-running-localtest-locally)
- [Changing test data](#changing-test-data)
- [Known issues](#known-issues)

### Prerequisites

Expand All @@ -14,89 +21,94 @@ These are some of the required steps, tips, and tricks when it comes to running
- Also
install [recommended extensions](https://code.visualstudio.com/docs/editor/extension-gallery#_workspace-recommended-extensions) (
f.ex. [C#](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp))
4. [Docker Desktop](https://www.docker.com/products/docker-desktop) (Linux users can also use native Docker)

4. [Podman Desktop](https://podman-desktop.io)/[Podman](https://podman.io) (Linux users can also use native Docker) or [Docker Desktop](https://www.docker.com/products/docker-desktop) (Windows and Mac) This might require you to purchase a license.
On mac with apple silicone (M1, M2, M3):
5. [vfkit](https://github.com/crc-org/vfkit?tab=readme-ov-file#installation)
### Setup

#### Using docker

1. Clone the `app-localtest` repository to a local folder and move into the folder.

```shell
git clone https://github.com/Altinn/app-localtest
cd app-localtest
```

2. Build and run the containers in the background.

```shell
docker compose up -d --build
```

:information_source: If you are using linux or mac you can use the Makefile to build and run the containers.

```shell
make docker-start-localtest
```

This mode supports running one app at a time. If you need to run multiple apps at once, stop the localtest container with `docker stop localtest` and follow the instructions below to run LocalTest locally outside Docker.
#### Clone the repository

3. Start your app
_This step requires that you have already [created an app](https://docs.altinn.studio/app/getting-started/create-app/), added a [data model](https://docs.altinn.studio/app/development/data/data-model/data-models-tool/), and [cloned the app](https://docs.altinn.studio/app/getting-started/local-dev/) to your local environment._
Move into the `App` folder of your application.
```shell
git clone https://github.com/Altinn/app-localtest
cd app-localtest
```

Example: If your application is named `my-awesome-app` and is located in the folder `C:\my_applications`, run the following command:
#### Option A: Start the containers using podman

```shell
cd C:\my_applications\my-awasome-app\App
```
This mode supports running one app at a time. If you need to run multiple apps at once, stop the localtest container with `podman stop localtest` and follow the instructions below to run LocalTest locally outside Docker/Podman.

Run the application:
> [!IMPORTANT]
> If you are using an mac with either a M1, M2 or M3 chip you may need to use `applehv` instead of `qemu` as the podman machine driver.
> This can be done by setting the environment variable `CONTAINERS_MACHINE_PROVIDER` to `applehv` before running the command below.
> To add this to your zsh profile run the following command: `echo "export CONTAINERS_MACHINE_PROVIDER=applehv" >> ~/.zprofile`
> If you are using Podman Desktop you also need to add these lines in `~/.config/containers/containers.conf` (check if the `[machine]` section already exists):
>
> ```
> [machine]
> provider = "applehv"
> ```
```shell
dotnet run
```
Start the containers with the following command:
#### Using podman

1. Clone the `app-localtest` repository to a local folder and move into the folder.

```shell
git clone https://github.com/Altinn/app-localtest
cd app-localtest
```

2. Build and run the containers in the background.

```shell
podman compose --file podman-compose.yml up -d --build
```

:information_source: If you are using linux or mac you can use the Makefile to build and run the containers.
```shell
podman compose --file podman-compose.yml up -d --build
```
```shell
make podman-start-localtest
```
> [!NOTE]
> If you are using linux or mac you can use the Makefile to build and run the containers.
>
> ```shell
> make podman-start-localtest
> ```
> [!IMPORTANT]
> Are you running podman version < 4.7.0 you need to use the following command instead:
>
> ```shell
> podman-compose --file podman-compose.yml up -d --build
> ```
>
> or the make command:
>
> ```shell
> make podman-compose-start-localtest
> ```
Localtest should now be runningn on port 8000 and can be accessed on <http://local.altinn.cloud:8000>.
#### Option B: Start the containers using Docker
This mode supports running one app at a time. If you need to run multiple apps at once, stop the localtest container with `docker stop localtest` and follow the instructions below to run LocalTest locally outside Docker.
```shell
docker compose up -d --build
```
> [!NOTE]
> If you are using linux or mac you can use the Makefile to build and run the containers.
>
> ```shell
> make docker-start-localtest
> ```
This mode supports running one app at a time. If you need to run multiple apps at once, stop the localtest container with `podman stop localtest` and follow the instructions below to run LocalTest locally outside Docker.
Localtest should now be runningn on port 80 and can be accessed on <http://local.altinn.cloud:80>.
3. Start your app
_This step requires that you have already [created an app](https://docs.altinn.studio/app/getting-started/create-app/), added a [data model](https://docs.altinn.studio/app/development/data/data-model/data-models-tool/), and [cloned the app](https://docs.altinn.studio/app/getting-started/local-dev/) to your local environment._
#### Start your app
_This step requires that you have already [created an app](https://docs.altinn.studio/app/getting-started/create-app/), added a [data model](https://docs.altinn.studio/app/development/data/data-model/data-models-tool/), and [cloned the app](https://docs.altinn.studio/app/getting-started/local-dev/) to your local environment._
Move into the `App` folder of your application.
Move into the `App` folder of your application.
Example: If your application is named `my-awesome-app` and is located in the folder `C:\my_applications`, run the following command:
Example: If your application is named `my-awesome-app` and is located in the folder `C:\my_applications`, run the following command:
```shell
cd C:\my_applications\my-awasome-app\App
```
```shell
cd C:\my_applications\my-awasome-app\App
```
Run the application:
Run the application:

```shell
dotnet run
```
```shell
dotnet run
```

The app and local platform services are now running locally. The app can be accessed on <http://local.altinn.cloud>.

Expand Down Expand Up @@ -203,3 +215,29 @@ This would be required if your app requires a role which none of the test users

4. Save and close the file
5. Restart LocalTest

### Known issues

#### Bind mounts folders gives permission denied. Nginx returns default page

On some nix systems you might experience problems with the bind mounts used by the containers. If you get the default nginx page when trying to access local.altinn.cloud this might be the case.

To verify this you can run the following command:

```shell
podman container exec -it localtest-loadbalancer cat /etc/nginx/templates/nginx.conf.conf
```

if you get a permission denied message this verifies that the bind mount is not working. A best effort fix for this is to run the following command:

```shell
make podman-selinux-bind-hack
```

#### Localtest reports that the app is not running even though it is

If localtest and you app is running, but localtest reports that the app is not running, it might be that the port is not open in the firewall.

You can verify if the app is running by opening `http://localhost:5005/<app-org-name>/<app-name>/swagger/index.html` (remember to replace `<app-org-name>` and `<app-name>` with the correct values).

If this is the case you can open a Windows Powershell as administrator and run the script `OpenAppPortInHyperVFirewall.ps1` located in the `scripts` folder.
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
- NGINX_HOST=localhost
- NGINX_PORT=80
- TEST_DOMAIN=${TEST_DOMAIN:-local.altinn.cloud}
- HOST_DOMAIN=host.docker.internal
- INTERNAL_DOMAIN=host.docker.internal
- ALTINN3LOCAL_PORT=${ALTINN3LOCAL_PORT:-80}
- NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx/
Expand Down
2 changes: 1 addition & 1 deletion loadbalancer/templates/nginx.conf.conf
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ http {
}

upstream app {
server ${INTERNAL_DOMAIN}:5005;
server ${HOST_DOMAIN}:5005;
}
# Redirect localhost and the old altinn3local.no to the configured test domain
server {
Expand Down
33 changes: 24 additions & 9 deletions podman-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,34 @@ networks:
external: false

services:
local.altinn.cloud:
localtest_loadbalancer:
container_name: localtest-loadbalancer
image: nginx:alpine-perl
restart: always
networks:
- altinntestlocal_network
altinntestlocal_network:
aliases:
- ${TEST_DOMAIN:-local.altinn.cloud}
ports:
- "${ALTINN3LOCAL_PORT:-8080}:80"
- "${ALTINN3LOCAL_PORT:-8000}:80"
environment:
- NGINX_HOST=localhost
- NGINX_PORT=80
- TEST_DOMAIN=${TEST_DOMAIN:-local.altinn.cloud}
- HOST_DOMAIN=host.docker.internal
- INTERNAL_DOMAIN=host.containers.internal
- ALTINN3LOCAL_PORT=${ALTINN3LOCAL_PORT:-80}
- ALTINN3LOCAL_PORT=${ALTINN3LOCAL_PORT:-8000}
- NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx/
- NGINX_ENVSUBST_TEMPLATE_SUFFIX=.conf
volumes:
- ./loadbalancer/templates:/etc/nginx/templates/:ro
- ./loadbalancer/www:/www/:ro
- type: bind
source: ./loadbalancer/templates/
target: /etc/nginx/templates/
read_only: true
- type: bind
source: ./loadbalancer/www/
target: /www/
read_only: true


altinn_platform_pdf:
Expand Down Expand Up @@ -57,11 +66,17 @@ services:
environment:
- DOTNET_ENVIRONMENT=Podman
- ASPNETCORE_URLS=http://*:5101/
- GeneralSettings__BaseUrl=http://${TEST_DOMAIN:-local.altinn.cloud}:${ALTINN3LOCAL_PORT:-80}
- GeneralSettings__BaseUrl=http://${TEST_DOMAIN:-local.altinn.cloud}:${ALTINN3LOCAL_PORT:-8000}
- GeneralSettings__HostName=${TEST_DOMAIN:-local.altinn.cloud}
volumes:
- ./testdata:/testdata
- ${ALTINN3LOCALSTORAGE_PATH:-AltinnPlatformLocal}:/AltinnPlatformLocal
- type: volume
source: AltinnPlatformLocal
target: /AltinnPlatformLocal/
read_only: false
- type: bind
source: ./testdata/
target: /testdata/
read_only: true

volumes:
AltinnPlatformLocal:
7 changes: 7 additions & 0 deletions scripts/OpenAppPortInHyperVFirewall.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
$AppPort = 5005
$NetFirewallHyperVName = '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}'

Get-NetFirewallHyperVVMCreator
Get-NetFirewallHyperVVMSetting -PolicyStore ActiveStore -Name $NetFirewallHyperVName
Get-NetFirewallHyperVRule -VMCreatorId $NetFirewallHyperVName
New-NetFirewallHyperVRule -Name Altinn3App -DisplayName "Altinn 3 Application" -Direction Inbound -VMCreatorId $NetFirewallHyperVName -Protocol TCP -LocalPorts $AppPort
4 changes: 4 additions & 0 deletions src/Configuration/LocalPlatformSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ public string LocalTestingStaticTestDataPath
}
}

public string LocalFrontendHostname { get; set; }

public string LocalFrontendProtocol { get; set; } = "http";

/// <summary>
/// Url for the local app when LocalAppMode == http
/// <summary>
Expand Down
25 changes: 13 additions & 12 deletions src/Controllers/FrontendVersionController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,8 @@
using System.Text.Json;

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

using LocalTest.Configuration;
using LocalTest.Models;
using LocalTest.Services.LocalApp.Interface;

using LocalTest.Services.TestData;
using LocalTest.Services.LocalFrontend.Interface;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc.Rendering;

Expand All @@ -23,7 +18,7 @@ public class FrontendVersionController : Controller
public static readonly string FRONTEND_URL_COOKIE_NAME = "frontendVersion";

[HttpGet]
public async Task<ActionResult> Index([FromServices] HttpClient client)
public async Task<ActionResult> Index([FromServices] HttpClient client, [FromServices] ILocalFrontendService localFrontendService)
{
var versionFromCookie = HttpContext.Request.Cookies[FRONTEND_URL_COOKIE_NAME];

Expand All @@ -36,14 +31,20 @@ public async Task<ActionResult> Index([FromServices] HttpClient client)
{
Text = "Keep as is",
Value = "",
},
new ()
{
Text = "localhost:8080 (local dev)",
Value = "http://localhost:8080/"
}
}
};
var groupLocalVersions = new SelectListGroup() { Name = "Frontend served from local dev server" };
var localFrontendPorts = await localFrontendService.GetLocalFrontendDevPorts();
foreach (var localPort in localFrontendPorts)
{
frontendVersion.Versions.Add(new SelectListItem()
{
Text = $"Local dev-server on port {localPort.Port} ({localPort.Branch} branch)",
Value = $"http://localhost:{localPort.Port}/",
Group = groupLocalVersions
});
}
var cdnVersionsString = await client.GetStringAsync("https://altinncdn.no/toolkits/altinn-app-frontend/index.json");
var groupCdnVersions = new SelectListGroup() { Name = "Specific version from cdn" };
var versions = JsonSerializer.Deserialize<List<string>>(cdnVersionsString)!;
Expand Down
7 changes: 7 additions & 0 deletions src/Models/LocalFrontendInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace LocalTest.Models;

public struct LocalFrontendInfo
{
public string Port { get; init; }
public string Branch { get; init; }
}
Loading

0 comments on commit 1d23de2

Please sign in to comment.