Skip to content

Commit

Permalink
feat(azure): add sql database support to Azure provider
Browse files Browse the repository at this point in the history
  • Loading branch information
HomelessDinosaur authored Jul 1, 2024
1 parent 385e651 commit e92356d
Show file tree
Hide file tree
Showing 12 changed files with 409 additions and 56 deletions.
6 changes: 6 additions & 0 deletions cloud/azure/cmd/runtime/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (

"github.com/nitrictech/nitric/cloud/azure/runtime/api"
"github.com/nitrictech/nitric/cloud/azure/runtime/resource"
postgresql "github.com/nitrictech/nitric/cloud/azure/runtime/sql"
"github.com/nitrictech/nitric/core/pkg/logger"

http_service "github.com/nitrictech/nitric/cloud/azure/runtime/gateway"
Expand Down Expand Up @@ -78,6 +79,11 @@ func main() {
logger.Errorf("Failed to load secret plugin: %s", err.Error())
}

membraneOpts.SqlPlugin, err = postgresql.New()
if err != nil {
logger.Errorf("Failed to load sql plugin: %s", err.Error())
}

membraneOpts.ResourcesPlugin = provider

m, err := membrane.New(membraneOpts)
Expand Down
4 changes: 2 additions & 2 deletions cloud/azure/deploy/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (

"github.com/getkin/kin-openapi/openapi3"
"github.com/pkg/errors"
apimanagement "github.com/pulumi/pulumi-azure-native-sdk/apimanagement"
apimanagement "github.com/pulumi/pulumi-azure-native-sdk/apimanagement/v2"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"

common "github.com/nitrictech/nitric/cloud/common/deploy/tags"
Expand Down Expand Up @@ -123,7 +123,7 @@ func (p *NitricAzurePulumiProvider) Api(ctx *pulumi.Context, parent pulumi.Resou

api, err := apimanagement.NewApi(ctx, ResourceName(ctx, name, ApiRT), &apimanagement.ApiArgs{
DisplayName: pulumi.String(displayName),
Protocols: apimanagement.ProtocolArray{"https"},
Protocols: pulumi.StringArray{pulumi.String("https")},
ApiId: pulumi.String(name),
Format: pulumi.String("openapi+json"),
Path: pulumi.String("/"),
Expand Down
12 changes: 10 additions & 2 deletions cloud/azure/deploy/containerenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func (p *NitricAzurePulumiProvider) newContainerEnv(ctx *pulumi.Context, name st
WorkspaceName: aw.Name,
})

res.ManagedEnv, err = app.NewManagedEnvironment(ctx, ResourceName(ctx, name, KubeRT), &app.ManagedEnvironmentArgs{
managementArgs := &app.ManagedEnvironmentArgs{
Location: p.ResourceGroup.Location,
ResourceGroupName: p.ResourceGroup.Name,
AppLogsConfiguration: app.AppLogsConfigurationArgs{
Expand All @@ -150,7 +150,15 @@ func (p *NitricAzurePulumiProvider) newContainerEnv(ctx *pulumi.Context, name st
},
},
Tags: pulumi.ToStringMap(common.Tags(p.StackId, ctx.Stack()+"Kube", resources.Service)),
}, pulumi.Parent(res))
}

if p.InfrastructureSubnet != nil {
managementArgs.VnetConfiguration = &app.VnetConfigurationArgs{
InfrastructureSubnetId: p.InfrastructureSubnet.ID(),
}
}

res.ManagedEnv, err = app.NewManagedEnvironment(ctx, ResourceName(ctx, name, KubeRT), managementArgs, pulumi.Parent(res))
if err != nil {
return nil, err
}
Expand Down
161 changes: 154 additions & 7 deletions cloud/azure/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ import (
"github.com/nitrictech/nitric/core/pkg/logger"
resourcespb "github.com/nitrictech/nitric/core/pkg/proto/resources/v1"
"github.com/pkg/errors"
apimanagement "github.com/pulumi/pulumi-azure-native-sdk/apimanagement"
apimanagement "github.com/pulumi/pulumi-azure-native-sdk/apimanagement/v2"
"github.com/pulumi/pulumi-azure-native-sdk/authorization"
"github.com/pulumi/pulumi-azure-native-sdk/containerinstance/v2"
"github.com/pulumi/pulumi-azure-native-sdk/dbforpostgresql/v2"
"github.com/pulumi/pulumi-azure-native-sdk/eventgrid"
"github.com/pulumi/pulumi-azure-native-sdk/keyvault"
"github.com/pulumi/pulumi-azure-native-sdk/network/v2"
"github.com/pulumi/pulumi-azure-native-sdk/resources"
"github.com/pulumi/pulumi-azure-native-sdk/storage"
"github.com/pulumi/pulumi-random/sdk/v4/go/random"
Expand Down Expand Up @@ -77,6 +80,15 @@ type NitricAzurePulumiProvider struct {

KeyValueStores map[string]*storage.Table

SqlMigrations map[string]*containerinstance.ContainerGroup
DatabaseServer *dbforpostgresql.Server
DbMasterPassword *random.RandomPassword
VirtualNetwork *network.VirtualNetwork

DatabaseSubnet *network.Subnet
InfrastructureSubnet *network.Subnet
ContainerGroupSubnet *network.Subnet

Roles *Roles
provider.NitricDefaultOrder
}
Expand Down Expand Up @@ -158,6 +170,132 @@ func createStorageAccount(ctx *pulumi.Context, group *resources.ResourceGroup, t
return storageAccount, nil
}

func (a *NitricAzurePulumiProvider) createDatabaseServer(ctx *pulumi.Context, tags map[string]string) error {
var err error

virtualNetworkName := "db-virtual-network"
a.VirtualNetwork, err = network.NewVirtualNetwork(ctx, virtualNetworkName, &network.VirtualNetworkArgs{
AddressSpace: &network.AddressSpaceArgs{
AddressPrefixes: pulumi.StringArray{
pulumi.String("10.0.0.0/16"),
},
},
FlowTimeoutInMinutes: pulumi.Int(10),
Location: pulumi.String(a.Region),
ResourceGroupName: a.ResourceGroup.Name,
VirtualNetworkName: pulumi.String(virtualNetworkName),
})
if err != nil {
return errors.WithMessage(err, "creating virtual network")
}

a.DatabaseSubnet, err = network.NewSubnet(ctx, "db-subnet", &network.SubnetArgs{
AddressPrefix: pulumi.String("10.0.0.0/18"),
ResourceGroupName: a.ResourceGroup.Name,
SubnetName: pulumi.String("db-subnet"),
VirtualNetworkName: a.VirtualNetwork.Name,
Delegations: network.DelegationArray{
network.DelegationArgs{
Name: pulumi.String("db-delegation"),
ServiceName: pulumi.String("Microsoft.DBforPostgreSQL/flexibleServers"),
},
},
})
if err != nil {
return errors.WithMessage(err, "creating database subnet")
}

a.InfrastructureSubnet, err = network.NewSubnet(ctx, "infrastructure-subnet", &network.SubnetArgs{
AddressPrefix: pulumi.String("10.0.64.0/18"),
ResourceGroupName: a.ResourceGroup.Name,
SubnetName: pulumi.String("infrastructure-subnet"),
VirtualNetworkName: a.VirtualNetwork.Name,
}, pulumi.DependsOn([]pulumi.Resource{a.DatabaseSubnet}))
if err != nil {
return errors.WithMessage(err, "creating infrastructure subnet")
}

a.ContainerGroupSubnet, err = network.NewSubnet(ctx, "container-group-subnet", &network.SubnetArgs{
AddressPrefix: pulumi.String("10.0.192.0/18"),
ResourceGroupName: a.ResourceGroup.Name,
SubnetName: pulumi.String("container-group-subnet"),
VirtualNetworkName: a.VirtualNetwork.Name,
Delegations: network.DelegationArray{
network.DelegationArgs{
Name: pulumi.String("container-instance-delegation"),
ServiceName: pulumi.String("Microsoft.ContainerInstance/containerGroups"),
},
},
}, pulumi.DependsOn([]pulumi.Resource{a.InfrastructureSubnet}))
if err != nil {
return errors.WithMessage(err, "creating container group subnet")
}

privateDns, err := network.NewPrivateZone(ctx, "db-private-dns", &network.PrivateZoneArgs{
ResourceGroupName: a.ResourceGroup.Name,
PrivateZoneName: pulumi.String("db-private-dns.postgres.database.azure.com"),
Location: pulumi.String("global"),
})
if err != nil {
return errors.WithMessage(err, "creating private dns zone")
}

vnetLink, err := network.NewVirtualNetworkLink(ctx, "db-private-dns-link", &network.VirtualNetworkLinkArgs{
Location: pulumi.String("global"),
PrivateZoneName: privateDns.Name,
RegistrationEnabled: pulumi.Bool(false),
ResourceGroupName: a.ResourceGroup.Name,
VirtualNetwork: &network.SubResourceArgs{
Id: a.VirtualNetwork.ID(),
},
VirtualNetworkLinkName: pulumi.String("db-private-dns-link"),
})
if err != nil {
return err
}

dbServerName := ResourceName(ctx, "", DatabaseServerRT)

// generate a db random password
a.DbMasterPassword, err = random.NewRandomPassword(ctx, "db-master-password", &random.RandomPasswordArgs{
Length: pulumi.Int(16),
Special: pulumi.Bool(false),
})
if err != nil {
return errors.WithMessage(err, "creating master password")
}

a.DatabaseServer, err = dbforpostgresql.NewServer(ctx, dbServerName, &dbforpostgresql.ServerArgs{
ResourceGroupName: a.ResourceGroup.Name,
Location: a.ResourceGroup.Location,
AdministratorLogin: pulumi.String("nitric"),
AdministratorLoginPassword: a.DbMasterPassword.Result,
CreateMode: pulumi.String(dbforpostgresql.CreateModeDefault),
AvailabilityZone: pulumi.String("1"),
Version: pulumi.String(dbforpostgresql.ServerVersion_14),
Network: &dbforpostgresql.NetworkArgs{
DelegatedSubnetResourceId: a.DatabaseSubnet.ID(),
PrivateDnsZoneArmResourceId: privateDns.ID(),
},
Sku: &dbforpostgresql.SkuArgs{
Name: pulumi.String("Standard_B1ms"),
Tier: pulumi.String(dbforpostgresql.SkuTierBurstable),
},
HighAvailability: &dbforpostgresql.HighAvailabilityArgs{
Mode: pulumi.String(dbforpostgresql.HighAvailabilityModeDisabled),
},
Storage: &dbforpostgresql.StorageArgs{
StorageSizeGB: pulumi.Int(32),
},
Tags: pulumi.ToStringMap(tags),
}, pulumi.DependsOn([]pulumi.Resource{a.DatabaseSubnet, privateDns, vnetLink}))
if err != nil {
return err
}

return nil
}

func hasResourceType(resources []*pulumix.NitricPulumiResource[any], resourceType resourcespb.ResourceType) bool {
for _, r := range resources {
if r.Id.GetType() == resourceType {
Expand Down Expand Up @@ -205,6 +343,14 @@ func (a *NitricAzurePulumiProvider) Pre(ctx *pulumi.Context, nitricResources []*
return errors.WithMessage(err, "resource group create")
}

if hasResourceType(nitricResources, resourcespb.ResourceType_SqlDatabase) {
logger.Info("Stack declares one or more databases, creating stack level PostgreSQL Database Server")
err := a.createDatabaseServer(ctx, tags.Tags(a.StackId, ctx.Stack(), commonresources.Stack))
if err != nil {
return errors.WithMessage(err, "create azure sql flexible server")
}
}

// Create a key vault if secrets are required.
// Unlike AWS and GCP which have centralized secrets management, Azure allows for multiple key vaults.
// This means we need to create a keyvault for each stack.
Expand Down Expand Up @@ -281,7 +427,7 @@ func (a *NitricAzurePulumiProvider) Result(ctx *pulumi.Context) (pulumi.StringOu
}).(pulumi.StringOutput)

if !ok {
return pulumi.StringOutput{}, fmt.Errorf("Failed to generate pulumi output")
return pulumi.StringOutput{}, fmt.Errorf("failed to generate pulumi output")
}

return output, nil
Expand All @@ -293,13 +439,14 @@ func NewNitricAzurePulumiProvider() *NitricAzurePulumiProvider {
principalsMap[resourcespb.ResourceType_Service] = map[string]*ServicePrincipal{}

return &NitricAzurePulumiProvider{
Apis: map[string]ApiResources{},
HttpProxies: map[string]ApiResources{},
Apis: make(map[string]ApiResources),
HttpProxies: make(map[string]ApiResources),
Buckets: make(map[string]*storage.BlobContainer),
Queues: make(map[string]*storage.Queue),
ContainerApps: map[string]*ContainerApp{},
Topics: map[string]*eventgrid.Topic{},
ContainerApps: make(map[string]*ContainerApp),
Topics: make(map[string]*eventgrid.Topic),
SqlMigrations: make(map[string]*containerinstance.ContainerGroup),
Principals: principalsMap,
KeyValueStores: map[string]*storage.Table{},
KeyValueStores: make(map[string]*storage.Table),
}
}
4 changes: 2 additions & 2 deletions cloud/azure/deploy/httpproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"github.com/nitrictech/nitric/cloud/common/deploy/resources"
deploymentspb "github.com/nitrictech/nitric/core/pkg/proto/deployments/v1"
"github.com/pkg/errors"
apimanagement "github.com/pulumi/pulumi-azure-native-sdk/apimanagement"
apimanagement "github.com/pulumi/pulumi-azure-native-sdk/apimanagement/v2"
"github.com/pulumi/pulumi-azure-native-sdk/managedidentity"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"

Expand Down Expand Up @@ -104,7 +104,7 @@ func (p *NitricAzurePulumiProvider) Http(ctx *pulumi.Context, parent pulumi.Reso

proxyApi, err := apimanagement.NewApi(ctx, ResourceName(ctx, name, ApiHttpProxyRT), &apimanagement.ApiArgs{
DisplayName: pulumi.Sprintf("%s-api", name),
Protocols: apimanagement.ProtocolArray{"https"},
Protocols: pulumi.StringArray{pulumi.String("https")},
ApiId: apiId,
Format: pulumi.String("openapi+json"),
Path: pulumi.String("/"),
Expand Down
2 changes: 2 additions & 0 deletions cloud/azure/deploy/resourcename.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ var (
ApiManagementProxyRT = ResourceType{Abbreviation: "httpproxy-mgmt", MaxLen: 80, AllowHyphen: true, AllowUpperCase: true, UseName: true}
// Alphanumerics and hyphens, Start with letter and end with alphanumeric.
ApiOperationPolicyRT = ResourceType{Abbreviation: "api-op-pol", MaxLen: 80, AllowUpperCase: true, AllowHyphen: true, UseName: true}
// Lowercase letters and numbers.
DatabaseServerRT = ResourceType{Abbreviation: "pg-svr", MaxLen: 24}
)

// cleanNameSegment removes all non-alphanumeric characters from a string.
Expand Down
23 changes: 14 additions & 9 deletions cloud/azure/deploy/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,14 +264,14 @@ func (p *NitricAzurePulumiProvider) Service(ctx *pulumi.Context, parent pulumi.R
Name: pulumi.String("TOLERATE_MISSING_SERVICES"),
Value: pulumi.String("true"),
},
// app.EnvironmentVarArgs{
// Name: pulumi.String("MONGODB_CONNECTION_STRING"),
// Value: args.MongoDatabaseConnectionString,
// },
// app.EnvironmentVarArgs{
// Name: pulumi.String("MONGODB_DATABASE"),
// Value: args.MongoDatabaseName,
// },
}

if p.DatabaseServer != nil {
env = append(env, app.EnvironmentVarArgs{
Name: pulumi.String("NITRIC_DATABASE_BASE_URL"),
Value: pulumi.Sprintf("postgres://%s:%s@%s:%s", "nitric", p.DbMasterPassword.Result,
p.DatabaseServer.FullyQualifiedDomainName, "5432"),
})
}

for k, v := range service.Env() {
Expand All @@ -287,6 +287,11 @@ func (p *NitricAzurePulumiProvider) Service(ctx *pulumi.Context, parent pulumi.R
// env = append(env, args.Env...)
// }

resOpts := []pulumi.ResourceOption{pulumi.Parent(res)}
for _, db := range p.SqlMigrations {
resOpts = append(resOpts, pulumi.DependsOn([]pulumi.Resource{db}))
}

// If this instance contains a schedule set the minimum instances to 1
// schedules rely on the Dapr Runtime to trigger the function, without a running instance the Dapr Runtime will not execute, so the schedule won't trigger.
_, schedulesFound := lo.Find(p.resources, func(item *pulumix.NitricPulumiResource[any]) bool {
Expand Down Expand Up @@ -365,7 +370,7 @@ func (p *NitricAzurePulumiProvider) Service(ctx *pulumi.Context, parent pulumi.R
},
},
},
}, pulumi.Parent(res))
}, resOpts...)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit e92356d

Please sign in to comment.