Skip to content

Commit

Permalink
Versioning Rules (#1896)
Browse files Browse the repository at this point in the history
* Mock versioning rules

* MVP of assignment rules table and header. just need to test when ready

* Merge in main and fix conflicts

* Fix rules type

* Refactor to use PollerIcon

* Bump to latest api version

* Update README for running Temporal server locally

* Remove test api, set notifyOnError to false

* Remove commented out mock
  • Loading branch information
Alex-Tideman authored May 2, 2024
1 parent 6caf9ec commit d27f01a
Show file tree
Hide file tree
Showing 18 changed files with 865 additions and 258 deletions.
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,49 @@ pnpn run build:docker
pnpn run preview:docker
```


### Running UI and Temporal Server locally

1. Build the ui-server image:

```diff
cd server
docker build -t my-ui-server .
```

2. In `temporal` repo
1. checkout feature branch or main
2. edit ui-server image name in `develop/docker-compose/docker-compose.yml`

```yaml
temporal-ui:
image: my-ui-server
```


And start the server dependencies

```yaml
make start-dependencies
```

3. Start the Temporal server

```yaml
make start
```

4. Change Vite port due to Grafana being on port 3000

```ts
server: {
port: 3001,
}
```




## Testing
We use [Playwright](https://playwright.dev) to interactively test the Temporal UI.

Expand Down
229 changes: 218 additions & 11 deletions go.work.sum

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions server/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ require (
github.com/labstack/echo/v4 v4.9.1
github.com/stretchr/testify v1.9.0
github.com/urfave/cli/v2 v2.3.0
go.temporal.io/api v1.29.1
golang.org/x/net v0.22.0
golang.org/x/oauth2 v0.16.0
google.golang.org/grpc v1.62.1
go.temporal.io/api v1.32.0
golang.org/x/net v0.24.0
golang.org/x/oauth2 v0.17.0
google.golang.org/grpc v1.63.2
google.golang.org/protobuf v1.33.0
gopkg.in/validator.v2 v2.0.0-20210331031555-b37d688a7fb0
gopkg.in/yaml.v3 v3.0.1
Expand All @@ -30,13 +30,13 @@ require (
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
)

Expand Down
32 changes: 16 additions & 16 deletions server/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -53,24 +53,24 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.temporal.io/api v1.29.1 h1:L722DCy3xCzpTe3Rvh1sFC9kcSaMJXqvodCF+swHGtQ=
go.temporal.io/api v1.29.1/go.mod h1:wZtsUJ3PySASGWbpXBWYVKJ4aHB2ZODEn/xNcTr9HRs=
go.temporal.io/api v1.32.0 h1:Jv0FieWDq0HJVqoHRE/kRHM+tIaRtR16RbXZZl+8Qb4=
go.temporal.io/api v1.32.0/go.mod h1:MClRjMCgXZTKmxyItEJPRR5NuJRBhSEpuF9wuh97N6U=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -84,8 +84,8 @@ golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand All @@ -104,12 +104,12 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 h1:8eadJkXbwDEMNwcB5O0s5Y5eCfyuCLdvaiOIaGTrWmQ=
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 h1:IR+hp6ypxjH24bkMfEJ0yHR21+gwPWdV+/IBrPQyn3k=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda h1:b6F6WIV4xHHD0FA4oIyzU6mHWg2WI2X1RBehwa5QN38=
google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda/go.mod h1:AHcE/gZH76Bk/ROZhQphlRoWo5xKDEtz3eVEO1LfA8c=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
Expand Down
47 changes: 47 additions & 0 deletions src/fixtures/task-queue-rules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"assignmentRules": [
{
"rule": {
"targetBuildId": "1.10",
"ramp": {
"percentageRamp": {
"rampPercentage": 0.2
}
}
},
"createTime": "2024-01-01T00:00:00Z"
},
{
"rule": {
"targetBuildId": "1.11",
"ramp": {
"percentageRamp": {
"rampPercentage": 0.4
}
}
},
"createTime": "2024-02-01T00:00:00Z"
},
{
"rule": {
"targetBuildId": "1.12",
"ramp": {
"percentageRamp": {
"rampPercentage": 0.5
}
}
},
"createTime": "2024-03-01T00:00:00Z"
}
],
"compatibleRedirectRules": [
{
"rule": {
"sourceBuildId": "1.12",
"targetBuildId": "1.13"
},
"createTime": "2024-01-01T00:00:00Z"
}
],
"conflictToken": "abcd1234"
}
14 changes: 14 additions & 0 deletions src/lib/components/poller-icon.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script lang="ts">
import Icon from '$lib/holocene/icon/icon.svelte';
import { translate } from '$lib/i18n/translate';
export let includesTaskQueueType: boolean;
</script>

<Icon
name={includesTaskQueueType ? 'checkmark' : 'close'}
class="m-auto {includesTaskQueueType ? 'text-blue-700' : 'text-primary'}"
title={includesTaskQueueType
? translate('common.yes')
: translate('common.no')}
/>
175 changes: 175 additions & 0 deletions src/lib/components/worker-compatibility.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
<script lang="ts">
import Badge from '$lib/holocene/badge.svelte';
import CompatibilityBadge from '$lib/holocene/compatibility-badge.svelte';
import EmptyState from '$lib/holocene/empty-state.svelte';
import TableHeaderRow from '$lib/holocene/table/table-header-row.svelte';
import TableRow from '$lib/holocene/table/table-row.svelte';
import Table from '$lib/holocene/table/table.svelte';
import { translate } from '$lib/i18n/translate';
import {
getBuildIdReachability,
type GetPollersResponse,
type TaskQueueCompatibility,
type WorkerReachability,
} from '$lib/services/pollers-service';
import { relativeTime, timeFormat } from '$lib/stores/time-format';
import { formatDate } from '$lib/utilities/format-date';
import {
getCurrentCompatibilityDefaultVersion,
getCurrentPollerBuildId,
getDefaultVersionForSet,
getNonDefaultVersionsForSet,
getOrderedVersionSets,
} from '$lib/utilities/task-queue-compatibility';
import PollerIcon from './poller-icon.svelte';
export let taskQueue: string;
export let workers: GetPollersResponse;
export let reachability: WorkerReachability | undefined = undefined;
export let compatibility: TaskQueueCompatibility | undefined = undefined;
$: versionSets = getOrderedVersionSets(compatibility);
$: defaultVersion = getCurrentCompatibilityDefaultVersion(compatibility);
</script>

<h2 class="text-base font-medium" data-testid="version-sets">
{translate('workers.version-sets')}
</h2>
<Table class="mb-6 w-full min-w-[600px] table-fixed">
<TableHeaderRow slot="headers">
<th class="w-3/12">{translate('workers.default')}</th>
<th class="w-9/12">{translate('workers.compatible-build-ids')}</th>
</TableHeaderRow>
{#each versionSets as set, index (index)}
<TableRow data-testid="version-row">
<td class="text-left" data-testid="version-default">
<CompatibilityBadge
defaultVersion
active={index === 0}
buildId={getDefaultVersionForSet(set.buildIds)}
>
<svelte:fragment slot="overall-default-worker">
{#if index === 0}{translate('workers.overall')}{/if}
</svelte:fragment>
<svelte:fragment slot="default-worker">
{translate('workers.default')}
</svelte:fragment>
</CompatibilityBadge>
</td>
<td class="text-left" data-testid="version-compatible-builds">
<div class="flex flex-wrap gap-2 font-mono">
{#each getNonDefaultVersionsForSet(set.buildIds) as buildId}
<CompatibilityBadge active={false} {buildId}>
<svelte:fragment slot="default-worker">
{translate('workers.default')}
</svelte:fragment>
</CompatibilityBadge>
{/each}
</div>
</td>
</TableRow>
{:else}
<tr class="w-full">
<td colspan="6">
<EmptyState title={translate('workers.no-version-sets-found')} />
</td>
</tr>
{/each}
</Table>
<h2 class="flex items-center gap-2 text-base font-medium" data-testid="workers">
{translate('workers.workers')}
<Badge type="count" class="rounded-sm">{workers?.pollers?.length || 0}</Badge>
</h2>
<Table class="mb-6 w-full min-w-[600px] table-fixed">
<caption class="sr-only" slot="caption"
>{translate('workflows.workers-tab')}</caption
>
<TableHeaderRow slot="headers">
<th class={reachability?.buildIdReachability?.length ? 'w-3/12' : 'w-6/12'}
>{translate('common.id')}</th
>
{#if reachability?.buildIdReachability?.length}
<th class="w-3/12">{translate('workers.version')}</th>
<th class="w-2/12">{translate('workers.retirability')}</th>
{/if}
<th class="w-2/12">{translate('workflows.last-accessed')}</th>
<th class="w-2/12">
<p class="text-center">
{translate('workflows.workflow-task-handler')}
</p>
</th>
<th class="w-2/12 text-center">
<p class="text-center">{translate('workflows.activity-handler')}</p>
</th>
</TableHeaderRow>
{#each workers?.pollers as poller (poller.identity)}
{@const buildId = getCurrentPollerBuildId(poller)}
{@const pollerReachability = getBuildIdReachability(
reachability,
taskQueue,
buildId,
)}
<TableRow data-testid="worker-row">
<td class="text-left" data-testid="worker-identity">
<p class="select-all">{poller.identity}</p>
</td>
{#if reachability?.buildIdReachability?.length}
<td class="text-left" data-testid="worker-identity">
<p class="select-all">
<CompatibilityBadge
defaultVersion={buildId === defaultVersion}
active={buildId === defaultVersion}
{buildId}
>
<svelte:fragment slot="overall-default-worker">
{#if buildId === defaultVersion}{translate(
'workers.overall',
)}{/if}
</svelte:fragment>
<svelte:fragment slot="default-worker">
{translate('workers.default')}
</svelte:fragment>
</CompatibilityBadge>
</p>
</td>
<td class="text-left" data-testid="worker-last-access-time">
<p>
<span class:reachability={!!pollerReachability}
>{pollerReachability}</span
>
</p>
</td>
{/if}
<td class="text-left" data-testid="worker-last-access-time">
<p class="select-all">
{formatDate(poller.lastAccessTime, $timeFormat, {
relative: $relativeTime,
})}
</p>
</td>
<td data-testid="workflow-poller">
<PollerIcon
includesTaskQueueType={poller.taskQueueTypes.includes('WORKFLOW')}
/>
</td>
<td data-testid="activity-poller">
<PollerIcon
includesTaskQueueType={poller.taskQueueTypes.includes('ACTIVITY')}
/>
</td>
</TableRow>
{:else}
<tr class="w-full">
<td colspan={reachability?.buildIdReachability?.length ? 8 : 6}>
<EmptyState title={translate('workflows.workers-empty-state')} />
</td>
</tr>
{/each}
</Table>

<style lang="postcss">
.reachability {
@apply rounded-sm bg-slate-200 px-2 py-1;
}
</style>
Loading

0 comments on commit d27f01a

Please sign in to comment.