diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4eaa7ca20fdc..be050b267c70 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -111,11 +111,12 @@ this should generally not be the case. Cluster API maintains the most recent release branch for all supported API and contract versions. Support for this section refers to the ability to backport and release patch versions. -| API Version | Branch | Supported Until | -| ------------- | ----------- | ---------- | -| **v1beta1** | release-1.0 | current stable | -| **v1alpha4** | release-0.4 | 2022-04-06 | -| **v1alpha3** | release-0.3 | 2022-02-23 | +| API Version | Branch | Supported Until | +| ------------- |-------------|-----------------| +| **v1beta1** | release-1.1 | current stable | +| **v1beta1** | release-1.0 | 2022-02-02 | +| **v1alpha4** | release-0.4 | 2022-04-06 | +| **v1alpha3** | release-0.3 | 2022-02-23 | - The API version is determined from the GroupVersion defined in the top-level `api/` package. - The EOL date is determined from the last release available once a new API version is published. diff --git a/docs/book/src/developer/architecture/controllers/cluster-topology.md b/docs/book/src/developer/architecture/controllers/cluster-topology.md index 6f9eb140411a..b1dd4bdfe698 100644 --- a/docs/book/src/developer/architecture/controllers/cluster-topology.md +++ b/docs/book/src/developer/architecture/controllers/cluster-topology.md @@ -16,4 +16,4 @@ The high level workflow of ClusterTopology reconciliation is shown below. ### Additional information -* See ClusterClass [proposal](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/202105256-cluster-class-and-managed-topologies.md#basic-behaviors) +* See ClusterClass [proposal](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20210526-cluster-class-and-managed-topologies.md#basic-behaviors) diff --git a/docs/book/src/developer/providers/implementers-guide/configure.md b/docs/book/src/developer/providers/implementers-guide/configure.md index 5b8d03e77510..6cecdc63453e 100644 --- a/docs/book/src/developer/providers/implementers-guide/configure.md +++ b/docs/book/src/developer/providers/implementers-guide/configure.md @@ -46,7 +46,7 @@ patchesStrategicMerge - manager_config.yaml ``` -[kustomizeyaml]: https://github.com/kubernetes-sigs/kustomize/blob/master/docs/glossary.md#kustomization +[kustomizeyaml]: https://kubectl.docs.kubernetes.io/references/kustomize/kustomization [patch]: https://git.k8s.io/community/contributors/devel/sig-api-machinery/strategic-merge-patch.md ## Our configuration diff --git a/docs/book/src/tasks/experimental-features/cluster-class/change-clusterclass.md b/docs/book/src/tasks/experimental-features/cluster-class/change-clusterclass.md index ea1c7b534ffd..fb8eda732fe0 100644 --- a/docs/book/src/tasks/experimental-features/cluster-class/change-clusterclass.md +++ b/docs/book/src/tasks/experimental-features/cluster-class/change-clusterclass.md @@ -114,7 +114,7 @@ potentially dangerous changes on those objects. -For additional info see [compatibility rules](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/202105256-cluster-class-and-managed-topologies.md#clusterclass-compatibility) +For additional info see [compatibility rules](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20210526-cluster-class-and-managed-topologies.md#clusterclass-compatibility) defined in the ClusterClass proposal. ## Planning ClusterClass changes diff --git a/docs/book/src/tasks/experimental-features/cluster-class/index.md b/docs/book/src/tasks/experimental-features/cluster-class/index.md index 063b5d923179..60851406abd6 100644 --- a/docs/book/src/tasks/experimental-features/cluster-class/index.md +++ b/docs/book/src/tasks/experimental-features/cluster-class/index.md @@ -8,7 +8,7 @@ ClusterClass is a powerful abstraction implemented on top of existing interfaces **Variable name to enable/disable the feature gate**: `CLUSTER_TOPOLOGY` Additional documentation: -* Background information: [ClusterClass and Managed Topologies CAEP](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/202105256-cluster-class-and-managed-topologies.md) +* Background information: [ClusterClass and Managed Topologies CAEP](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20210526-cluster-class-and-managed-topologies.md) * For ClusterClass authors: * [Writing a ClusterClass](./write-clusterclass.md) * [Changing a ClusterClass](./change-clusterclass.md) diff --git a/tilt_modules/extensions.json b/tilt_modules/extensions.json new file mode 100644 index 000000000000..896c383ce303 --- /dev/null +++ b/tilt_modules/extensions.json @@ -0,0 +1,9 @@ +{ + "Extensions": [ + { + "Name": "uibutton", + "ExtensionRegistry": "https://github.com/tilt-dev/tilt-extensions", + "TimeFetched": "2022-02-24T17:51:20.645793+01:00" + } + ] +} \ No newline at end of file diff --git a/tilt_modules/uibutton/README.md b/tilt_modules/uibutton/README.md new file mode 100644 index 000000000000..c4ba23323b66 --- /dev/null +++ b/tilt_modules/uibutton/README.md @@ -0,0 +1,205 @@ +# UIButton + +Authors: + * [Matt Landis](https://github.com/landism) + * [Milas Bowman](https://github.com/milas) + +Extend the Tilt UI with custom actions for your resources. + +## Example Usage + +```python +load('ext://uibutton', 'cmd_button', 'location', 'text_input') + +# define resource 'my-resource' +# k8s_resource('my-resource') + +# create a button on resource 'my-resource' +cmd_button(name='my-resource-hello-world', + resource='my-resource', + argv=['echo', 'Hello my-resource!'], + text='Hello World', + icon_name='travel_explore') + +# create a button in the navbar +# (logs will go to Tiltfile) +cmd_button(name='nav-hello-world', + argv=['echo', 'Hello nav!'], + text='Hello World', + location=location.NAV, + icon_name='waving_hand') + +# create a button with a text field input +cmd_button(name='foo', + resource='my-resource', + text='Reseed database', + inputs=[ + text_input('SHARD'), + ], + # If you need env var expansion *within the command itself* + # you'll need to run it via a shell. + argv=['/bin/sh', '-c', './reseed_database.sh --shard="$SHARD"'], + ) +``` + +## API + +### `cmd_button` +```python +cmd_button( + name, + resource, + argv, + text=None, + location=location.RESOURCE, + icon_name=None, + icon_svg=None, + inputs=[], +) +``` + +Creates a button for a resource that runs the given command when clicked. + +| Argument | Type | Description | +|-------------|----------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------| +| `name` | `str` | Unique ID for button | +| `resource` | `str` | Resource to associate button with (required if `location=location.RESOURCE`) | +| `argv` | `List[str]` | Local command to run when button is clicked | +| `text` | `str` | Text to display on button (optional: defaults to `name`) | +| `location` | `str` (enum) | Button placement in UI (see `location` section below) | +| `icon_name` | `str` | Name of [Material Icons font ligature][material-icons-font] to use as icon (at most one of `icon_name` or `icon_svg` should be specified) | +| `icon_svg` | `str` or `Blob` | `` to use as icon; should have 24x24px viewport (at most one of `icon_name` or `icon_svg` should be specified) | +| `inputs` | `List[typing.Union[text_input, bool_input]]` | Form inputs for butdown (optional) | + +### `location` +To specify button location, you can bind the `location` helper type or pass a string, e.g. `location=location.NAV` and `location='nav'` are equivalent. + +| Location | `str` Value | `location` Value | +| ---------- | ----------- | ------------------- | +| Resource | `resource` | `location.RESOURCE` | +| Global nav | `nav` | `location.NAV` | + +### `text_input` +```python +text_input(name, label="", default="", placeholder="") +``` +Specifies that the button's UI will include a text field the user can enter. +The field's current value will be set in the command's env when it is run. + +| Argument | Type | Description | +| ------------- | ----- | ----------- | +| `name` | `str` | The text input's name. Also the name of the environment variable to be set when running the command. | +| `label` | `str` | Text to display next to the text input in the UI. | +| `default` | `str` | Default initial value for this field. | +| `placeholder` | `str` | A short hint that describes the expected input of this field. | + +### `bool_input` +```python +(name, label='', default=False, true_string=None, false_string=None) +``` +Specifies that the button's UI will include a checkbox to toggle this value. +When the command is run, an environment variable will be set based on the +checkbox's state. By default, the variable will be set to the string `"true"` or `"false"`, as appropriate. Those values can be configured with the `true_string` and `false_string` parameters. + +| Argument | Type | Description | +| -------------- | ----- | ----------- | +| `name` | `str` | The input's name. Also the name of the environment variable to be set when running the command. | +| `label` | `str` | Text to display next to the input in the UI. | +| `default` | `bool` | Default initial value for this field. | +| `true_string` | `str` | If not None, when the checkbox is checked, the environment variable will be set to this string instead of "true". | +| `false_string` | `str` | If not None, when the checkbox is checked, the environment variable will be set to this string instead of "false". | + +## Button Placement +Currently, you can create buttons for a specific resource, which will be shown with other resource contextual actions such as "Copy Pod ID" or as part of the global nav next to the help and account buttons. + +### Resource +To create a resource button, pass the resource name via `resource='my-resource'` and omit the `location` argument or explicitly pass `location=location.RESOURCE`. + +Any command output will appear interleaved with the associated resource's logs. + +Providing an icon is optional. + +### Global Nav +To create a global nav button, pass `location=location.NAV`. +Optionally, you can pass a resource name via `resource='my-resource'`. + +Any command output will appear interleaved with the associated resource's logs if specified and the `(Tiltfile)` resource if not. + +Global nav buttons SHOULD specify an icon via either the `icon_name` or `icon_svg` arguments. +The `text` value will appear on hover. + +## Icons +Button icons can either come from the set of built-in icons that ship with Tilt or a custom SVG. + +Navbar buttons SHOULD include an icon as the button text is only visible on hover. +For resource buttons, icons are optional and will appear within the button if specified. + +If both `icon_name` and `icon_svg` are specified, `icon_svg` will take precedence. + +### Built-in Icons (Material Icons) +Tilt includes the [Material Icons][material-icons-font] by default. +Use the `icon_name` argument and pass the "font ligature" value for your desired icon. +The font ligatures are visible in the sidebar after clicking on an icon on the Material Fonts site. + +> **💡 Tip**: They are `lower_snake_case` values, e.g. the "Check Circle" icon has a font ligature value of `check_circle`. + +### Custom Icons (SVG) +Use the `icon_svg` argument and pass a full `` element. +The SVG viewport should be 24x24 for best results. + +To avoid string quoting issues, it's often easiest to load the SVG from disk rather than storing it directly in your Tiltfile: +```python +load('ext://uibutton', 'cmd_button', 'location') + +icon = read_file('./icon.svg') + +cmd_button('svg-btn', + argv=['echo', '✨ Hello from SVG ✨'], + location=location.NAV, + icon_svg=icon, + text='SVG Nav Button') # text will appear on hover +``` + +## Inputs +If a button has inputs, the UI will attach an arrow to the button, allowing the +user to set those inputs' values. +When the button is clicked, those input's values will be set as environment +variables in the executed process. +For example: +```python +cmd_button('hello', + argv=['sh', '-c', 'echo Hello, $NAME'], + location=location.NAV, + icon_name='front_hand', + text='Hello!', + inputs=[ + text_input('NAME', placeholder='Enter your name'), + ] +) +``` + +This will create a button (top right) with an options menu (opened by the little arrow): +![screenshot of button with options menu](assets/button_with_input.png) + +When the user clicks the button to run its command, `$NAME` will be set +to the field's value, e.g.: + +``` +Running cmd: echo Hello, $NAME +Hello, there +``` + +Note that if the command needs an env var expanded inside the command itself (e.g., it directly uses `$NAME`, rather than simply invoking a program that uses `$NAME`), it will need to be wrapped in a shell call, e.g. ['sh', '-c', 'mycommand $NAME']. + +## Other notes + +Commands are executed locally on the host running `tilt up` (similar to `local_resource`). + +The `argv` argument only accepts a list, e.g. `['echo', 'Hello World']` but not `echo 'Hello World'`. + +To run a script, invoke the interpreter and then pass the script as an argument, e.g. `['bash', '-c', 'echo "Hello from bash ${BASH_VERSION}"']`. + +## Known Issues +* Renamed/deleted buttons will not be removed until Tilt is restarted ([#193](https://github.com/tilt-dev/tilt-extensions/issues/193)) + +[material-icons-font]: https://fonts.google.com/icons diff --git a/tilt_modules/uibutton/Tiltfile b/tilt_modules/uibutton/Tiltfile new file mode 100644 index 000000000000..d348acca4d16 --- /dev/null +++ b/tilt_modules/uibutton/Tiltfile @@ -0,0 +1,150 @@ +LOCATION_RESOURCE = 'resource' +LOCATION_NAV = 'nav' + +location = struct( + RESOURCE=LOCATION_RESOURCE, + NAV=LOCATION_NAV, +) + +valid_subcommands = ['up', 'ci'] + +def _button(name, location, text='', icon=None, annotations={}, inputs=[]): + text = text or name + btn = { + "apiVersion": "tilt.dev/v1alpha1", + "kind": "UIButton", + "metadata": { + "name": name, + "annotations": annotations + }, + "spec": { + "text": text, + "location": { + "componentType": location.type, + "componentID": location.id, + } + } + } + + if len(inputs): + btn["spec"]["inputs"] = inputs + + if icon: + if icon.svg: + # convert to str to handle str + Blob + btn['spec']['iconSVG'] = str(icon.svg) + elif icon.name: + btn['spec']['iconName'] = icon.name + + return btn + + +def cmd_button(name, resource='', argv=[], text=None, + location=LOCATION_RESOURCE, icon_name=None, icon_svg=None, + inputs=[]): + if config.tilt_subcommand not in valid_subcommands: + return + + if not location: + fail('location is required') + + if not resource: + if location == LOCATION_RESOURCE: + fail('Must provide a resource name') + else: + # for nav buttons, default logs to Tiltfile if no resource specified + resource = '(Tiltfile)' + + if not argv: + fail('argv cannot be empty') + + btn_annotations = {} + + if location == LOCATION_NAV: + location = struct(type='Global', id='nav') + elif location == LOCATION_RESOURCE: + location = struct(type='Resource', id=resource) + btn_annotations['tilt.dev/resource'] = resource + else: + # fallback to simplify experimenting with new locations in the future + loc_type, sep, loc_id = location.partition('/') + if not sep: + fail('Unsupported location {}'.format(location)) + location = struct(type=loc_type, id=loc_id) + + button = _button( + name=name, + location=location, + text=text, + icon=struct(name=icon_name, svg=icon_svg), + annotations=btn_annotations, + inputs=inputs, + ) + cmd = { + "apiVersion": "tilt.dev/v1alpha1", + "kind": "Cmd", + "metadata": { + "name": "btn-" + name, + "annotations": { + "tilt.dev/resource": resource, + "tilt.dev/log-span-id": 'cmd:' + name, + } + }, + "spec": { + "args": argv, + "dir": config.main_dir, + "startOn": { + "startAfter": _now(), + "uiButtons": [name], + }, + } + } + + local( + command='echo "${TILT_APPLY_YAML}"\ + | %s apply -f -' % (sys.executable,), + command_bat='(cmd /v:on /c echo !TILT_APPLY_YAML!)\ + | %s apply -f -' % (sys.executable,), + env={'TILT_APPLY_YAML': str(encode_yaml_stream([button, cmd]))}, + echo_off=True) + + +def _now(): + return str(local( + # this is portable across coreutils/busybox/BSD + # note: it's missing fractional seconds because that's not available + # from strftime + command='date -u +"%Y-%m-%dT%H:%M:%S.000000Z"', + command_bat="powershell Get-Date (Get-Date).ToUniversalTime()\ + -UFormat '+%Y-%m-%dT%H:%M:%S.000000Z'", + echo_off=True, + quiet=True) + ).rstrip('\r\n') + + +def text_input(name, label='', default='', placeholder=''): + return { + "name": name, + "label": label, + "text": { + "defaultValue": default, + "placeholder": placeholder, + } + } + + +def bool_input(name, label='', default=False, true_string=None, + false_string=None): + result = { + "name": name, + "label": label, + "bool": { + "defaultValue": default, + }, + } + if true_string != None: + result["bool"]["trueString"] = true_string + if false_string != None: + result["bool"]["falseString"] = false_string + + return result diff --git a/tilt_modules/uibutton/assets/button_with_input.png b/tilt_modules/uibutton/assets/button_with_input.png new file mode 100644 index 000000000000..1fa5853e66a4 Binary files /dev/null and b/tilt_modules/uibutton/assets/button_with_input.png differ diff --git a/tilt_modules/uibutton/test/Tiltfile b/tilt_modules/uibutton/test/Tiltfile new file mode 100644 index 000000000000..a3ecd8f0351e --- /dev/null +++ b/tilt_modules/uibutton/test/Tiltfile @@ -0,0 +1,19 @@ +load('../Tiltfile', 'cmd_button', 'location', 'text_input') + +local_resource('vigoda', cmd='echo "Hello from resource"') + +cmd_button("resource-button", resource="vigoda", + argv=["bash", "-c", "echo Hello from bash ${BASH_VERSION}"], + text='Resource Button', icon_name='star') + +goose_icon = read_file('./goose.svg') + +# nav-button-svg specifies no resource (goes to Titlfile log) +cmd_button('nav-button-svg', argv=["echo", "✨ Hello from SVG ✨"], + location=location.NAV, icon_svg=goose_icon, text='SVG Nav Button') +# nav-button-std specifies a resource so logs go there +cmd_button('nav-button-std', argv=["echo", "Hello from nav"], + location=location.NAV, resource='vigoda', icon_name='calendar_today') +cmd_button('nav-button-hello', argv=["sh", "-c", "echo Hello, $NAME"], + location=location.NAV, icon_name='front_hand', text='Hello', + inputs=[text_input('NAME')]) diff --git a/tilt_modules/uibutton/test/goose.svg b/tilt_modules/uibutton/test/goose.svg new file mode 100644 index 000000000000..18e41e01ce47 --- /dev/null +++ b/tilt_modules/uibutton/test/goose.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tilt_modules/uibutton/test/test.sh b/tilt_modules/uibutton/test/test.sh new file mode 100755 index 000000000000..3794ef557191 --- /dev/null +++ b/tilt_modules/uibutton/test/test.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd "$(dirname "$0")" + +set -ex +tilt ci +tilt down --delete-namespaces + +tilt alpha tiltfile-result >/dev/null