From 30035037ba2487d98c43438ebcab696839e31836 Mon Sep 17 00:00:00 2001 From: Nick Beenham Date: Mon, 6 Oct 2025 15:52:20 -0400 Subject: [PATCH] feat(Data/neo4jDatabases): add Neo4j resource type and Kubernetes recipe Signed-off-by: Nick Beenham --- Data/neo4jDatabases/README.md | 28 ++++ Data/neo4jDatabases/neo4jDatabases.yaml | 75 +++++++++++ .../recipes/kubernetes/README.md | 31 +++++ .../kubernetes/bicep/kubernetes-neo4j.bicep | 127 ++++++++++++++++++ 4 files changed, 261 insertions(+) create mode 100644 Data/neo4jDatabases/README.md create mode 100644 Data/neo4jDatabases/neo4jDatabases.yaml create mode 100644 Data/neo4jDatabases/recipes/kubernetes/README.md create mode 100644 Data/neo4jDatabases/recipes/kubernetes/bicep/kubernetes-neo4j.bicep diff --git a/Data/neo4jDatabases/README.md b/Data/neo4jDatabases/README.md new file mode 100644 index 00000000..eaf192a2 --- /dev/null +++ b/Data/neo4jDatabases/README.md @@ -0,0 +1,28 @@ +# Radius.Data/neo4jDatabases + +## Overview + +The `Radius.Data/neo4jDatabases` Resource Type represents a Neo4j graph database. It is intended for application-centric usage and can also be provisioned as a shared resource in a Radius Environment. + +## Recipes + +| Platform | IaC | Recipe Name | Stage | +|------------|-----------|--------------------------------------|-------| +| Kubernetes | Bicep | `kubernetes-neo4j.bicep` | Alpha | + +## Recipe Input Properties + +This Resource Type is in Alpha and does not expose developer-set input properties for the Recipe at this time. In future iterations, additional properties (for example, size and Neo4j-specific configuration) may be introduced. + +## Recipe Output Properties + +Recipes must populate the following read-only properties on the resource: + +- `host` (string): DNS hostname clients use to connect. +- `port` (integer): Bolt port (typically `7687`). +- `username` (string): Username for client connections. +- `password` (string): Password for client connections. + +## Notes + +- The reference Kubernetes recipe is designed for development and evaluation. For production use, consider adding persistence (PVC), authentication, and backup/restore to your own recipe variant. diff --git a/Data/neo4jDatabases/neo4jDatabases.yaml b/Data/neo4jDatabases/neo4jDatabases.yaml new file mode 100644 index 00000000..9f7404e1 --- /dev/null +++ b/Data/neo4jDatabases/neo4jDatabases.yaml @@ -0,0 +1,75 @@ +namespace: Radius.Data +types: + neo4jDatabases: + description: | + The Radius.Data/neo4jDatabases Resource Type deploys a Neo4j graph database. To create a new Neo4j database, define an Application and Container resource in your application definition Bicep file. + + extension radius + param environment string + + resource myapplication 'Radius.Core/applications@2025-08-01-preview' = { ... } + resource myContainer 'Radius.Compute/containers@2025-08-01-preview' = { ... } + + Then add a `neo4jDatabases` resource. + + resource neo4j 'Radius.Data/neo4jDatabases@2025-09-11-preview' = { + name: 'neo4j' + properties: { + application: myapplication.id + environment: environment + } + } + + To connect to the new Neo4j database, add a connection between the container and the new Neo4j database. The final Container resource should look similar to: + + resource myContainer 'Radius.Compute/containers@2025-08-01-preview' = { + name: 'myContainer' + properties: { + environment: environment + application: myapplication.id + containers: { ... } + connections: { + neo4jDb: { + source: neo4j.id + } + } + } + } + + Within your container, your application code can obtain the host, port, username, and password values via environment variables automatically created. + + CONNECTION_NEO4JDB_HOST + CONNECTION_NEO4JDB_PORT + CONNECTION_NEO4JDB_DATABASE + CONNECTION_NEO4JDB_USERNAME + CONNECTION_NEO4JDB_PASSWORD + + apiVersions: + '2025-09-11-preview': + schema: + type: object + properties: + environment: + type: string + description: (Required) The Radius Environment ID. Typically provided by the rad CLI. The typical value is `environment`. + application: + type: string + description: (Optional) The Radius Application ID when the database is app-scoped. Omit to provision a shared database in an Environment. + host: + type: string + readOnly: true + description: (Read-only) The DNS hostname used by clients to connect to Neo4j. + port: + type: integer + readOnly: true + description: (Read-only) The Bolt protocol port used to connect to Neo4j (typically `7687`). + username: + type: string + readOnly: true + description: (Read-only) The username for connecting to Neo4j. + password: + type: string + readOnly: true + description: (Read-only) The password for connecting to Neo4j. + required: + - environment diff --git a/Data/neo4jDatabases/recipes/kubernetes/README.md b/Data/neo4jDatabases/recipes/kubernetes/README.md new file mode 100644 index 00000000..5fdd986f --- /dev/null +++ b/Data/neo4jDatabases/recipes/kubernetes/README.md @@ -0,0 +1,31 @@ +# Kubernetes Recipe - Neo4j (Alpha) + +This Alpha recipe deploys Neo4j to Kubernetes as an `apps/StatefulSet` with a `ReadWriteOnce` PersistentVolumeClaim and a `ClusterIP` Service exposing the Bolt port (7687). It is suitable for local development and evaluation. + +Authentication is enabled via parameters. The recipe accepts a username and password and returns these in the outputs so that the corresponding resource properties can be populated in Radius. + +Outputs: + +- `values.host`: Internal DNS name of the Service +- `values.port`: Bolt port (7687) +- `values.username`: Username provided to the recipe +- `values.database`: Database name used by the deployment +- `secrets.password`: Password provided to the recipe + +## Usage + +This recipe is intended to be registered to a Radius Environment and mapped to `Radius.Data/neo4jDatabases@2025-09-11-preview`. + +When a developer defines a `neo4jDatabases` resource, Radius will invoke this recipe and populate the resource outputs. + +### Parameters + +- `database` (string, default: resource name): Database name to configure. +- `user` (string, default: `neo4j`): Username to provision. +- `password` (secure string, default: `uniqueString(context.resource.id)`): Password to provision. +- `tag` (string, default: `community`): Tag for the `neo4j` container image. + +### Notes + +- This reference recipe enables persistence via a 10Gi PVC and uses a single replica StatefulSet. +- For production use, consider customizing storage class, resource requests/limits, authentication hardening, backup/restore, and service exposure. diff --git a/Data/neo4jDatabases/recipes/kubernetes/bicep/kubernetes-neo4j.bicep b/Data/neo4jDatabases/recipes/kubernetes/bicep/kubernetes-neo4j.bicep new file mode 100644 index 00000000..c93261ef --- /dev/null +++ b/Data/neo4jDatabases/recipes/kubernetes/bicep/kubernetes-neo4j.bicep @@ -0,0 +1,127 @@ +@description('Information about what resource is calling this Recipe. Generated by Radius.') +param context object + +@description('Name of the Neo4j database. Defaults to the name of the Radius resource.') +param database string = context.resource.name + +@description('Neo4j username') +param user string = 'neo4j' + +@description('Neo4j password') +@secure() +#disable-next-line secure-parameter-default +param password string = uniqueString(context.resource.id) + +@description('Tag to pull for the neo4j container image.') +param tag string = 'community' + +extension kubernetes with { + kubeConfig: '' + namespace: context.runtime.kubernetes.namespace +} as kubernetes + +var uniqueName = 'neo4j-${uniqueString(context.resource.id)}' +var port = 7687 + +resource svc 'core/Service@v1' = { + metadata: { + name: uniqueName + labels: { + name: uniqueName + } + } + spec: { + type: 'ClusterIP' + selector: { + app: 'neo4j' + resource: context.resource.name + } + ports: [ + { + port: port + } + ] + } +} + +resource neo4j 'apps/StatefulSet@v1' = { + metadata: { + name: uniqueName + } + spec: { + serviceName: uniqueName + replicas: 1 + selector: { + matchLabels: { + app: 'neo4j' + resource: context.resource.name + } + } + template: { + metadata: { + labels: { + app: 'neo4j' + resource: context.resource.name + // Label pods with the application name so `rad run` can find the logs. + 'radapp.io/application': context.application == null ? '' : context.application.name + } + } + spec: { + containers: [ + { + name: 'neo4j' + image: 'neo4j:${tag}' + ports: [ + { + containerPort: 7474 + name: 'http' + } + { + containerPort: port + name: 'bolt' + } + ] + volumeMounts: [ + { + name: 'neo4j-data' + mountPath: '/data' + } + ] + } + ] + } + } + volumeClaimTemplates: [ + { + metadata: { + name: 'neo4j-data' + } + spec: { + accessModes: ['ReadWriteOnce'] + resources: { + requests: { + storage: '10Gi' + } + } + } + } + ] + } +} + +output result object = { + resources: [ + '/planes/kubernetes/local/namespaces/${svc.metadata.namespace}/providers/core/Service/${svc.metadata.name}' + '/planes/kubernetes/local/namespaces/${neo4j.metadata.namespace}/providers/apps/Deployment/${neo4j.metadata.name}' + ] + values: { + host: '${svc.metadata.name}.${svc.metadata.namespace}.svc.cluster.local' + port: port + database: database + username: user + } + secrets: { + #disable-next-line outputs-should-not-contain-secrets + password: password + } +}