diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..82a3030 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,52 @@ +name: Create and publish SAM Dispatch + +on: + push: + branches: ["main"] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + attestations: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GHCR_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image + id: push + uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v2 + with: + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}} + subject-digest: ${{ steps.push.outputs.digest }} + push-to-registry: true diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml new file mode 100644 index 0000000..709aee8 --- /dev/null +++ b/.github/workflows/python.yml @@ -0,0 +1,27 @@ +# .github/workflows/test.yml +name: Test + +on: + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest + pip install -e . + + - name: Run tests + run: pytest diff --git a/README.md b/README.md index 6a53876..aa8aaac 100644 --- a/README.md +++ b/README.md @@ -1 +1,46 @@ # sam-dispatch + +## Usage + +``` +$ sam-dispatch --help +usage: sam-dispatch [-h] config + +Automated setup of test clients + +positional arguments: + config Path to config + +options: + -h, --help show this help message and exit +``` + +## Example config + +```jsonc +{ + "name": "Example Scenario", // name of scenario + "address": "127.0.0.1:8080", // port to run dispatcher on + "clients": 1, // how many clients to register + /* How many groups of clients that should communicate. + Each group has at least one denim client that communicates with a denim client from another group. + No other clients communicate across groups. + groups is a distribution so if you want 3 groups you would set it up as: + [0.3, 0.4, 0.3] + this creates 3 groups where two have 30% of the client population and one have 40% of the client population + */ + "groups": [1], + "tickMillis": 1000, // how many milliseconds one tick corresponds to + "durationTicks": 500, // time of experiment + "messageSizeRange": [200, 500], // the size range clients will be sending messages in + "denimProbability": 1, // how probable a client will be to send a denim message + "sendRateRange": [1, 5], // how fast a client will send a message, each client gets a random send rate, faster send rate will have less data in the messages and vice versa + "startEpoch": 10, // seconds to start, when all clients are ready + "report": "report.json" // final report is saved to report/.json +} +``` + +## Docker + +1. build: `docker build --network=host -t sam-dispatch .` +2. run `docker run --rm -p 8080:8080 -v ./reports:/reports sam-dispatch example.json` diff --git a/src/sam_dispatcher/server.py b/src/sam_dispatcher/server.py index f47ec24..d225423 100644 --- a/src/sam_dispatcher/server.py +++ b/src/sam_dispatcher/server.py @@ -36,17 +36,15 @@ async def upload(request: Request, report: ClientReport): def main(): global state - parser = ArgumentParser("sam-dispatch") - parser.add_argument("scenario", help="Path to scenario toml") - parser.add_argument("-a", "--address", default="127.0.0.1:8080") - parser.add_argument("-r", "--reload", action="store_true") + parser = ArgumentParser( + "sam-dispatch", description="Automated setup of test clients" + ) + parser.add_argument("config", help="Path to config") args = parser.parse_args() - scenario_path: str = args.scenario - addr: str = args.address - ip, port = addr.split(":") + config_path: str = args.config - state = State(scenario_path) + state = State(config_path) - uvicorn.run( - "sam_dispatcher.server:app", host=ip, port=int(port), reload=args.reload - ) + ip, port = state.scenario.address.split(":") + + uvicorn.run("sam_dispatcher.server:app", host=ip, port=int(port)) diff --git a/src/sam_dispatcher/state.py b/src/sam_dispatcher/state.py index d896c5f..f5002d4 100644 --- a/src/sam_dispatcher/state.py +++ b/src/sam_dispatcher/state.py @@ -9,6 +9,7 @@ class Scenario(BaseModel): name: str + address: str = Field(alias="address", default="127.0.0.1:8080") clients: int = Field(alias="clients") groups: List[float] = Field(alias="groups") tick_millis: int = Field(alias="tickMillis")