Skip to content

Commit

Permalink
Add support for Redis for simplified init (#2904)
Browse files Browse the repository at this point in the history
Add support for Redis as an in-memory DB.

Python and JS libraries are autodetected. The bicep generated will host Redis using Azure Container Apps' managed add-on.

Tests have been added to validate bicep correctness which largely covers the feature. Manual tests were ran additionally to verify the Container App functionality.

Fixes #2902
  • Loading branch information
weikanglim committed Oct 27, 2023
1 parent 76e99cf commit a8bfac4
Show file tree
Hide file tree
Showing 14 changed files with 85 additions and 2 deletions.
3 changes: 3 additions & 0 deletions cli/azd/internal/appdetect/appdetect.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ const (
DbMongo DatabaseDep = "mongo"
DbMySql DatabaseDep = "mysql"
DbSqlServer DatabaseDep = "sqlserver"
DbRedis DatabaseDep = "redis"
)

func (db DatabaseDep) Display() string {
Expand All @@ -114,6 +115,8 @@ func (db DatabaseDep) Display() string {
return "MySQL"
case DbSqlServer:
return "SQL Server"
case DbRedis:
return "Redis"
}

return ""
Expand Down
2 changes: 2 additions & 0 deletions cli/azd/internal/appdetect/appdetect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func TestDetect(t *testing.T) {
DbMongo,
DbMySql,
DbPostgres,
DbRedis,
DbSqlServer,
},
},
Expand All @@ -80,6 +81,7 @@ func TestDetect(t *testing.T) {
DbMongo,
DbMySql,
DbPostgres,
DbRedis,
},
},
{
Expand Down
2 changes: 2 additions & 0 deletions cli/azd/internal/appdetect/javascript.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ func (nd *javaScriptDetector) DetectProject(path string, entries []fs.DirEntry)
databaseDepMap[DbPostgres] = struct{}{}
case "tedious":
databaseDepMap[DbSqlServer] = struct{}{}
case "redis", "redis-om":
databaseDepMap[DbRedis] = struct{}{}
}
}

Expand Down
2 changes: 2 additions & 0 deletions cli/azd/internal/appdetect/python.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ func (pd *pythonDetector) DetectProject(path string, entries []fs.DirEntry) (*Pr
"beanie",
"motor":
databaseDepMap[DbMongo] = struct{}{}
case "redis", "redis-om":
databaseDepMap[DbRedis] = struct{}{}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"mongodb": "^5.7.0",
"mysql": "^2.18.1",
"pg-promise": "^11.5.3",
"tedious": "^16.4.0"
"tedious": "^16.4.0",
"redis": "^4.6.10"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ psycopg2
pymongo
psycopg2-binary
beanie
redis
1 change: 1 addition & 0 deletions cli/azd/internal/repository/app_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var languageMap = map[appdetect.Language]project.ServiceLanguageKind{
var dbMap = map[appdetect.DatabaseDep]struct{}{
appdetect.DbMongo: {},
appdetect.DbPostgres: {},
appdetect.DbRedis: {},
}

var ErrNoServicesDetected = errors.New("no services detected in the current directory")
Expand Down
10 changes: 10 additions & 0 deletions cli/azd/internal/repository/detect_confirm.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package repository

import (
"context"
"errors"
"fmt"
"log"
"path/filepath"
"time"

"github.com/AlecAivazis/survey/v2/terminal"
"github.com/azure/azure-dev/cli/azd/internal/appdetect"
"github.com/azure/azure-dev/cli/azd/internal/tracing"
"github.com/azure/azure-dev/cli/azd/internal/tracing/fields"
Expand Down Expand Up @@ -124,12 +126,18 @@ func (d *detectConfirm) Confirm(ctx context.Context) error {
return nil
case 1:
if err := d.remove(ctx); err != nil {
if errors.Is(err, terminal.InterruptErr) {
continue
}
return err
}

tracing.IncrementUsageAttribute(fields.AppInitModifyRemoveCount.Int(1))
case 2:
if err := d.add(ctx); err != nil {
if errors.Is(err, terminal.InterruptErr) {
continue
}
return err
}

Expand Down Expand Up @@ -175,6 +183,8 @@ func (d *detectConfirm) render(ctx context.Context) error {
recommendedServices = append(recommendedServices, "Azure Database for PostgreSQL flexible server")
case appdetect.DbMongo:
recommendedServices = append(recommendedServices, "Azure CosmosDB API for MongoDB")
case appdetect.DbRedis:
recommendedServices = append(recommendedServices, "Azure Container Apps Redis add-on")
}

status := ""
Expand Down
8 changes: 8 additions & 0 deletions cli/azd/internal/repository/infra_confirm.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ func (i *Initializer) infraSpecFromDetect(
detect detectConfirm) (scaffold.InfraSpec, error) {
spec := scaffold.InfraSpec{}
for database := range detect.Databases {
if database == appdetect.DbRedis { // no configuration needed for redis
continue
}

dbPrompt:
for {
dbName, err := i.console.Prompt(ctx, input.ConsoleOptions{
Expand Down Expand Up @@ -122,6 +126,10 @@ func (i *Initializer) infraSpecFromDetect(
serviceSpec.DbPostgres = &scaffold.DatabaseReference{
DatabaseName: spec.DbPostgres.DatabaseName,
}
case appdetect.DbRedis:
serviceSpec.DbRedis = &scaffold.DatabaseReference{
DatabaseName: "redis",
}
}
}
spec.Services = append(spec.Services, serviceSpec)
Expand Down
14 changes: 14 additions & 0 deletions cli/azd/internal/scaffold/scaffold_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,20 @@ func TestExecInfra(t *testing.T) {
},
},
},
{
"API with Redis",
InfraSpec{
Services: []ServiceSpec{
{
Name: "api",
Port: 3100,
DbRedis: &DatabaseReference{
DatabaseName: "redis",
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion cli/azd/internal/scaffold/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ type ServiceSpec struct {
// Back-end properties
Backend *Backend

// Connection to a database. Only one should be set.
// Connection to a database
DbPostgres *DatabaseReference
DbCosmosMongo *DatabaseReference
DbRedis *DatabaseReference
}

type Frontend struct {
Expand Down
34 changes: 34 additions & 0 deletions cli/azd/resources/scaffold/templates/host-containerapp.bicept
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ param databaseName string
@secure()
param databasePassword string
{{- end}}
{{- if .DbRedis}}
param redisName string
{{- end}}
{{- if (and .Frontend .Frontend.Backends)}}
param apiUrls array
{{- end}}
Expand Down Expand Up @@ -74,6 +77,29 @@ module fetchLatestImage '../modules/fetch-container-image.bicep' = {
name: name
}
}
{{- if .DbRedis}}

resource redis 'Microsoft.App/containerApps@2023-04-01-preview' = {
name: redisName
location: location
properties: {
environmentId: containerAppsEnvironment.id
configuration: {
service: {
type: 'redis'
}
}
template: {
containers: [
{
image: 'redis'
name: 'redis'
}
]
}
}
}
{{- end}}

resource app 'Microsoft.App/containerApps@2023-04-01-preview' = {
name: name
Expand Down Expand Up @@ -190,6 +216,14 @@ resource app 'Microsoft.App/containerApps@2023-04-01-preview' = {
}
}
]
{{- if .DbRedis}}
serviceBinds: [
{
serviceId: redis.id
name: 'redis'
}
]
{{- end}}
scale: {
minReplicas: 1
maxReplicas: 10
Expand Down
3 changes: 3 additions & 0 deletions cli/azd/resources/scaffold/templates/main.bicept
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ module {{bicepName .Name}} './app/{{.Name}}.bicep' = {
containerRegistryName: registry.outputs.name
exists: {{bicepName .Name}}Exists
appDefinition: {{bicepName .Name}}Definition
{{- if .DbRedis}}
redisName: 'rd-{{containerAppName .Name}}-${resourceToken}'
{{- end}}
{{- if .DbCosmosMongo}}
cosmosDbConnectionString: vault.getSecret(cosmosDb.outputs.connectionStringKey)
{{- end}}
Expand Down
1 change: 1 addition & 0 deletions cli/azd/resources/scaffold/templates/next-steps.mdt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
{{- range .Services}}
- [app/{{.Name}}.bicep](./infra/app/{{.Name}}.bicep)
{{- end}}
3. For services using Redis, environment variables will not show up under `env` explicitly, but are available as: `REDIS_ENDPOINT`, `REDIS_HOST`, `REDIS_PASSWORD`, and `REDIS_PORT`.

### Provision infrastructure and deploy application code

Expand Down

0 comments on commit a8bfac4

Please sign in to comment.