Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@

# Default rule: anything that doesn't match a more specific rule goes here
* @radius-project/maintainers-resource-types-contrib @radius-project/approvers-resource-types-contrib

# Allows on-call members to respond to dependabot updates to workflows.
.github/workflows/* @radius-project/on-call @radius-project/maintainers-resource-types-contrib @radius-project/approvers-resource-types-contrib
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ spec:
containers:
- image: mcr.microsoft.com/azurelinux/base/nginx:1.25
name: nginx
# nginx will serve files found in this directory.
# nginx will serve files found in this directory
volumeMounts:
- name: content
mountPath: /usr/share/nginx/html
Expand Down
2 changes: 1 addition & 1 deletion build/validation.mk → .github/build/validation.mk
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ publish-test-terraform-recipes: ## Publishes terraform recipes to the current Ku
rm -rf "$$temp_recipes_dir"

@echo -e "$(ARROW) Deploying web server..."
kubectl apply -f ./build/tf-module-server/resources.yaml -n $(TERRAFORM_MODULE_SERVER_NAMESPACE)
kubectl apply -f ./.github/build/tf-module-server/resources.yaml -n $(TERRAFORM_MODULE_SERVER_NAMESPACE)

@echo -e "$(ARROW) Waiting for web server to be ready..."
kubectl rollout status deployment.apps/tf-module-server -n $(TERRAFORM_MODULE_SERVER_NAMESPACE) --timeout=600s
Expand Down
31 changes: 31 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## Description
<!--
Brief description of the changes in this PR. Give enough context for the reviewer to understand why the change is being made.
-->

Related GitHub Issue: <!--#issue_number or N/A-->

## Testing
<!--
Describe how a reviewer should test these changes.
-->

## Contributor Checklist

- [ ] File names follow naming conventions and folder structure
- [ ] Platform engineer documentation is in README.md
- [ ] Developer documentation is the top-level description property
- [ ] Example of defining the Resource Type is in the developer documentation
- [ ] Example of using the Resource Type with a Container is in the developer documentation
- [ ] Verified the output of `rad resource-type show` is correct
- [ ] All properties in the Resource Type definition have clear descriptions
- [ ] Enum properties have values defined in `enum: []`
- [ ] Required properties are listed in `required: []` for every object property (not just the top-level properties)
- [ ] Properties about the deployed resource, such as connection strings, are defined as read-only properties and are marked as `readOnly: true`
- [ ] Recipes include a results output variable with all read-only properties set
- [ ] Environment-specific parameters, such as a vnet ID, are exposed for platform engineers to set in the Environment
- [ ] Recipes use the [Recipe context object](https://docs.radapp.io/reference/context-schema/) when possible
- [ ] Recipes are provided for at least one platform
- [ ] Recipes handle secrets securely
- [ ] Recipes are idempotent
- [ ] Resource types and recipes were tested
17 changes: 13 additions & 4 deletions .github/scripts/update-bicepconfig.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
#!/bin/bash
set -e

# Script: Update bicepconfig.json with published extensions
# This script finds all published .tgz files and adds them to bicepconfig.json
# This script creates a fresh bicepconfig.json with all published .tgz files

echo "Updating bicepconfig.json with published extensions..."
echo "Creating bicepconfig.json with published extensions..."

# Create base bicepconfig.json with required experimental features
cat > bicepconfig.json << 'EOF'
{
"extensions": {
"radius": "br:biceptypes.azurecr.io/radius:latest",
"aws": "br:biceptypes.azurecr.io/aws:latest"
}
}
EOF

# Find all published .tgz files and add them to bicepconfig.json (safe handling)
tgz_files=()
Expand All @@ -29,7 +38,7 @@ if [[ ${#tgz_files[@]} -gt 0 ]]; then
mv bicepconfig.tmp bicepconfig.json
done

echo "✅ Successfully updated bicepconfig.json with extensions"
echo "✅ Successfully created bicepconfig.json with extensions"
else
echo "No extension .tgz files found to add to bicepconfig.json"
fi
Expand Down
17 changes: 12 additions & 5 deletions .github/scripts/validate-common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,21 @@ find_yaml_files() {
printf '%s\n' "${yaml_files[@]}"
}

# Find recipe files with specific pattern
# Find recipe files with specific pattern (only in configured resource folders)
find_recipe_files() {
local pattern="$1"
local recipe_files=()

while IFS= read -r -d '' f; do
recipe_files+=("$f")
done < <(find . -path "$pattern" -type f -print0)
for folder in "${resource_folders[@]}"; do
if [[ -d "./$folder" ]]; then
echo "Searching for recipes in folder: $folder" >&2
while IFS= read -r -d '' f; do
recipe_files+=("$f")
done < <(find "./$folder" -path "$pattern" -type f -print0)
else
echo "Folder $folder does not exist, skipping recipe search..." >&2
fi
done

printf '%s\n' "${recipe_files[@]}"
}
Expand Down Expand Up @@ -342,7 +349,7 @@ test_recipes() {
done

# Deploy test application for this resource type
test_app_path="$root_folder/$resource_type/app.bicep"
test_app_path="$root_folder/$resource_type/test/app.bicep"
deployment_name="test-${root_folder,,}-${platform_service}-${template_kind}-$(date +%s)"

deploy_and_cleanup_test_app "$test_app_path" "$deployment_name" "for $platform_service ($template_kind recipe)"
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/validate-resource-types.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ name: Validate Resource Types
on:
push:
branches: [ main ]
paths:
- '**/test/**'
pull_request:
branches: [ main ]
paths:
- '**/test/**'
workflow_dispatch:
inputs:
version:
Expand All @@ -20,9 +24,9 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v5
- name: Setup Node
uses: actions/setup-node@v3
uses: actions/setup-node@v5
with:
node-version: 16
- name: Download k3d
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
./*.tgz
**/*.tgz
**/bicepconfig.json
38 changes: 38 additions & 0 deletions Compute/routes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## Overview

The Radius.Compute/routes Resource Type defines network routes for responding to external clients. It is always part of a Radius Application. It is analogous to a Kubernetes HTTPRoute, TCPRoute, TLSRoute, or UDPRoute resource.

Developer documentation is embedded in the Resource Type definition YAML file. Developer documentation is accessible via `rad resource-type show Radius.Compute/routes`.

## Recipes

A list of available Recipes for this Resource Type, including links to the Bicep and Terraform templates:

|Platform| IaC Language| Recipe Name | Stage |
|---|---|---|---|
| TODO | TODO | TODO | Alpha |

## Recipe Input Properties

| Radius Property | Kubernetes Property |
|---|---|
| context.properties.kind | Used by Recipe to determine which Kubernetes resource type to create (HTTPRoute, TCPRoute, TLSRoute, or UDPRoute). |
| context.properties.hostnames[] | HTTPRoute.spec.hostnames[] |
| context.properties.rules[] | HTTPRoute.spec.rules[] |
| context.properties.rules[].matches[] | HTTPRoute.spec.rules[].matches[] |
| context.properties.rules[].matches[].httpHeaders[].* | HTTPRoute.spec.rules[].matches[].headers[].* |
| context.properties.rules[].matches[].httpMethod | HTTPRoute.spec.rules[].matches[].method |
| context.properties.rules[].matches[].httpPath | HTTPRoute.spec.rules[].matches[].path.value |
| context.properties.rules[].matches[].httpQueryParams[].* | HTTPRoute.spec.rules[].matches[].queryParams[].* |
| context.properties.rules[].destinationContainer | N/A |
| context.properties.rules[].destinationContainer.resourceId | N/A |
| context.properties.rules[].destinationContainer.containerName | N/A |
| context.properties.rules[].destinationContainer.containerPortName | N/A |

## Recipe Output Properties

### result.listener

- result.listener.hostname: The hostname of the listener. The listener hostname plus the paths defined by the developer constitute the URL.
- result.listener.port: The port of the listener
- result.listener.protocol: The protocol of the listener
193 changes: 193 additions & 0 deletions Compute/routes/routes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
namespace: Radius.Compute
types:
routes:
description: |
The Radius.Compute/routes Resource Type defines network routes for responding to external clients. Note that a Routes resource is not required for service-to-service communication. To use Routes, define a Container and ensure a `containerPort` is specified.

extension radius
param environment string

resource myApplication 'Radius.Core/applications@2025-08-01-preview' = { ... }

resource myContainer 'Radius.Compute/containers@2025-08-01-preview' = {
name: 'myContainer'
properties: {
environment: environment
application: myApplication.id
containers: {
frontend: {
image: 'frontend:1.25'
ports: {
web: {
containerPort: 8080
}
}
}
accounts: {
image: 'accounts:1.25'
ports: {
web: {
containerPort: 8080
}
}
}
}
}
}

Then define a Routes resource.

resource ingressRule 'Radius.Compute/routes@2025-08-01-preview' = {
name: 'ingressRule'
properties: {
application: myApplication.id
environment: environment
kind: 'HTTP'
rules: [
{
matches: [
{
httpPath: '/'
}
]
destinationContainer: {
resourceId: myContainer.id
containerName: 'frontend'
containerPortName: 'web'
}
}
]
}
}

The hostname is determined by the Recipe.

Multiple rules can be included in Routes.

resource ingressRule 'Radius.Compute/routes@2025-08-01-preview' = {
name: 'ingressRule'
properties: {
application: myApplication.id
environment: environment
kind: 'HTTP'
rules: [
{
matches: [
{
httpPath: '/'
}
]
destinationContainer: {
resourceId: myContainer.id
containerName: 'frontend'
containerPortName: 'web'
}
}
{
matches: [
{
httpPath: '/accounts'
}
]
destinationContainer: {
resourceId: myContainer.id
containerName: 'accounts'
containerPortName: 'web'
}
}
]
}
}

apiVersions:
'2025-08-01-preview':
schema:
type: object
properties:
environment:
type: string
description: (Required) The Radius Environment ID. Typically set by the rad CLI. Typically value should be `environment`.
application:
type: string
description: (Required) The Radius Application ID. `myApplication.id` for example.
kind:
type: string
enum: [HTTP, TCP, TLS, UDP]
description: (Optional) The type of rule. If not specified, `HTTPRoute` is assumed. `HTTPRoute` provides L7 ingress with support for matching based on the hostname and HTTP header. `TCPRoute` provides L4 ingress with no support for matching (all traffic is forwarded to the Container). `TLSRoute` provides L4 ingress with the ability to match based on Server Name Indication (SNI) which is equivalent to hostname in TLS. `UDPRoute` is similar to TCPRoutes but uses UDP.
hostnames:
type: array
description: (Optional) Use only when kind is HTTP or TLS. When HTTP, match against the HTTP Host header. When using TLS, match against the SNI attribute of TLS ClientHello message. Hostname may be preceded by a * wildcard.
items:
type: string
rules:
type: array
description: (Required) Rules define semantics for matching a network connection request based on conditions and forwarding the request to a Container.
items:
type: object
properties:
matches:
type: array
description: (Required) Matches define conditions used for matching a request.
items:
type: object
properties:
httpHeaders:
type: array
description: (Optional) HTTP headers to match. Specify only when kind is HTTP. Multiple match values are ANDed together. A request must match all the specified headers to match.
items:
type: object
properties:
name:
type: string
description: (Required) The name of the HTTP Header to be matched. Must be exact.
value:
type: string
description: (Required) Value of HTTP Header to be matched.
required: [name, value]
httpMethod:
type: string
description: (Optional) The HTTP method to match. Specify only when kind is HTTP.
enum: [GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH]
httpPath:
type: string
description: (Optional) The HTTP request path to match. Trailing space is ignored. Requests for `/abc`, `/abc/`, and ``/abc/def/` will all match `/abc`.
httpQueryParams:
type: array
description: (Optional) HTTP query parameters to match. Specify only when kind is HTTP.
items:
type: object
properties:
name:
type: string
description: (Required) Name of the HTTP query parameter to be matched. Specify only when kind is HTTP.
value:
type: string
description: (Required) Value of the HTTP query parameter to be matched.
required: [name, value]
destinationContainer:
type: object
properties:
resourceId:
type: string
description: (Required) The Radius Container resource ID.
containerName:
type: string
description: (Required) The specific container to target within the Container resource.
containerPortName:
type: string
description: (Required) The port name to target from the container.
required: [resourceId, containerName, containerPortName]
required: [matches, destinationContainer]
listener:
type: object
description: (Read Only) The Gateway Listener the route is assigned to.
readOnly: true
properties:
hostname:
type: string
port:
type: integer
protocol:
type: string
enum: [HTTP, HTTPS, TLS, TCP, UDP]
required: [environment, application, rules]
Loading