Skip to content
Open
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
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,29 @@ resource "render_service" "nextjs" {
}
}


resource "render_service" "python_app" {
name = "my_python_app"
repo = "https://github.com/render-examples/flask-hello-world"
type = "web_service"
branch = "master"

web_service_details = {
env = "python"
region = "frankfurt"
plan = "starter"
native = {
build_command = "pip install -r requirements.txt"
start_command = "gunicorn app:app"
}
disk = {
name = "my_app_data"
mount_path = "/my_app_data"
size_gb = 10
}
}
}

resource "render_service" "mongodb" {
name = "mongodb"
repo = "https://github.com/render-examples/mongodb"
Expand Down
2 changes: 1 addition & 1 deletion examples/deploy-flask/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ resource "render_service" "flask" {
branch = "master"

web_service_details = {
env = "node"
env = "python"
region = "frankfurt"
plan = "starter"
native = {
Expand Down
130 changes: 122 additions & 8 deletions render/models/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,19 @@ type Service struct {
Type types.String `tfsdk:"type"`
Repo types.String `tfsdk:"repo"`
Branch types.String `tfsdk:"branch"`
Image *ImageDetails `tfsdk:"image"`
Owner types.String `tfsdk:"owner"`
AutoDeploy types.Bool `tfsdk:"auto_deploy"`
WebServiceDetails *WebServiceDetails `tfsdk:"web_service_details"`
StaticSiteDetails *StaticSiteDetails `tfsdk:"static_site_details"`
PrivateServiceDetails *PrivateServiceDetails `tfsdk:"private_service_details"`
EnvVars *[]EnvVar `tfsdk:"env_vars"`
}

type EnvVar struct {
Key types.String `tfsdk:"key"`
Value types.String `tfsdk:"value"`
Generated types.Bool `tfsdk:"generated"`
}

type WebServiceDetails struct {
Expand All @@ -28,6 +36,19 @@ type WebServiceDetails struct {
HealthCheckPath types.String `tfsdk:"health_check_path"`
Native *WebServiceDetailsNative `tfsdk:"native"`
Url types.String `tfsdk:"url"`
Disk *Disk `tfsdk:"disk"`
Docker *WebServiceDetailsDocker `tfsdk:"docker"`
}

type ImageDetails struct {
OwnerId types.String `tfsdk:"owner_id"`
ImagePath types.String `tfsdk:"image_path"`
RegistryCredentialId types.String `tfsdk:"registry_credential_id"`
}
type WebServiceDetailsDocker struct {
DockerCommand types.String `tfsdk:"command"`
DockerContext types.String `tfsdk:"context"`
DockerfilePath types.String `tfsdk:"path"`
}

type WebServiceDetailsNative struct {
Expand Down Expand Up @@ -61,12 +82,22 @@ func (s Service) FromResponse(response render.Service) Service {
serviceType := *response.Type

service := Service{
ID: fromStringOptional(response.Id),
Name: fromStringOptional(response.Name),
Type: fromServiceType(response.Type),
Repo: fromStringOptional(response.Repo),
Branch: fromStringOptional(response.Branch),
Owner: fromStringOptional(response.OwnerId),
ID: fromStringOptional(response.Id),
Name: fromStringOptional(response.Name),
Type: fromServiceType(response.Type),
Repo: fromStringOptional(response.Repo),
Branch: fromStringOptional(response.Branch),
Owner: fromStringOptional(response.OwnerId),
AutoDeploy: fromBoolOptional(response.AutoDeploy, true), //sub-optimal
EnvVars: s.EnvVars,
}

if response.ImagePath != nil {
service.Image = &ImageDetails{
OwnerId: fromStringOptional(response.OwnerId),
ImagePath: fromStringOptional(response.ImagePath),
RegistryCredentialId: fromStringOptional(response.RegistryCredentialId),
}
}

if serviceType == render.WebService {
Expand All @@ -82,12 +113,37 @@ func (s Service) FromResponse(response render.Service) Service {

native, err := details.EnvSpecificDetails.AsNativeEnvironmentDetails()

if err == nil {
if err == nil && s.WebServiceDetails.Native != nil {
service.WebServiceDetails.Native = &WebServiceDetailsNative{
BuildCommand: fromStringOptional(native.BuildCommand),
StartCommand: fromStringOptional(native.StartCommand),
}
}
if details.Disk != nil {
service.WebServiceDetails.Disk = &Disk{
Name: fromStringOptional(details.Disk.Name),

// Hack because the OpenAPI doesn't specify these fields as return.. I should check this
MountPath: s.WebServiceDetails.Disk.MountPath,
SizeGB: s.WebServiceDetails.Disk.SizeGB,
}
}
docker, err := details.EnvSpecificDetails.AsDockerDetails()
if err == nil {
dockerContext := fromStringOptional(docker.DockerContext)
if dockerContext == types.StringNull() || dockerContext == types.StringValue("") {
dockerContext = s.WebServiceDetails.Docker.DockerContext
}
dockerPath := fromStringOptional(docker.DockerfilePath)
if dockerPath == types.StringNull() || dockerPath == types.StringValue("") {
dockerPath = s.WebServiceDetails.Docker.DockerfilePath
}
service.WebServiceDetails.Docker = &WebServiceDetailsDocker{
DockerCommand: fromStringOptional(docker.DockerCommand),
DockerContext: dockerContext,
DockerfilePath: dockerPath,
}
}
}

if serviceType == render.PrivateService {
Expand Down Expand Up @@ -130,11 +186,49 @@ func (s Service) ToServicePOST(ownerId string) (*render.ServicePOST, error) {
service := render.ServicePOST{
Type: serviceType,
Name: s.Name.ValueString(),
Repo: s.Repo.ValueString(),
Repo: stringOptionalNil(s.Repo),
Branch: stringOptionalNil(s.Branch),
OwnerId: ownerId,
}

if s.Image != nil {
service.Image = &render.ImagePOST{
ImagePath: s.Image.ImagePath.ValueString(),
OwnerId: ownerId,
RegistryCredentialId: stringOptionalNil(s.Image.RegistryCredentialId),
}
}

if s.EnvVars != nil {
var envVars []render.ServicePOST_EnvVars_Item
for _, envVar := range *s.EnvVars {
item := render.ServicePOST_EnvVars_Item{}
if envVar.Generated.ValueBool() {
err := item.FromEnvVarKeyGenerateValue(render.EnvVarKeyGenerateValue{
Key: envVar.Key.ValueString(),
GenerateValue: "true",
})

if err != nil {
return nil, err
}

} else {
err := item.FromEnvVarKeyValue(render.EnvVarKeyValue{
Key: envVar.Key.ValueString(),
Value: envVar.Value.ValueString(),
})
if err != nil {
return nil, err
}
}

envVars = append(envVars, item)

}
service.EnvVars = &envVars
}

serviceDetails := render.ServicePOST_ServiceDetails{}

if serviceType == render.WebService || s.WebServiceDetails != nil {
Expand Down Expand Up @@ -324,6 +418,18 @@ func toWebServiceDetails(webServiceDetails *WebServiceDetails) (map[string]inter
details["envSpecificDetails"] = native
}

if webServiceDetails.Disk != nil {
details["disk"] = toDisk(webServiceDetails.Disk)
}

if webServiceDetails.Docker != nil {
details["envSpecificDetails"] = map[string]interface{}{
"dockerCommand": webServiceDetails.Docker.DockerCommand.ValueString(),
"dockerContext": webServiceDetails.Docker.DockerContext.ValueString(),
"dockerfilePath": webServiceDetails.Docker.DockerfilePath.ValueString(),
}
}

return details, nil
}

Expand Down Expand Up @@ -398,6 +504,14 @@ func fromIntOptional(num *int) types.Int64 {
return types.Int64Value(int64(*num))
}

func fromBoolOptional(str *render.ServiceAutoDeploy, defaultVal bool) types.Bool {
if str == nil {
return types.BoolValue(defaultVal)
}

return types.BoolValue(*str == "yes" || *str == "true")
}

func fromStringOptional(str *string) types.String {
if str == nil {
return types.StringNull()
Expand Down
33 changes: 32 additions & 1 deletion render/resources/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ func (r *serviceResource) Schema(_ context.Context, req resource.SchemaRequest,
},
}

docker := schema.SingleNestedAttribute{
Optional: true,
Attributes: map[string]schema.Attribute{
"command": schema.StringAttribute{Optional: true},
"context": schema.StringAttribute{Optional: true},
"path": schema.StringAttribute{Optional: true},
},
}

resp.Schema = schema.Schema{
Description: `Provider for service resource`,
Attributes: map[string]schema.Attribute{
Expand All @@ -75,8 +84,28 @@ func (r *serviceResource) Schema(_ context.Context, req resource.SchemaRequest,
"type": schema.StringAttribute{Required: true, PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}},
"branch": schema.StringAttribute{Optional: true, Computed: true},
"auto_deploy": schema.BoolAttribute{Optional: true},
"repo": schema.StringAttribute{Required: true, PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}},
"repo": schema.StringAttribute{Optional: true, PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}},
"owner": schema.StringAttribute{Optional: true, Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}},
"env_vars": schema.ListNestedAttribute{
Description: "Service environment variable",
Optional: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"key": schema.StringAttribute{Required: true},
"value": schema.StringAttribute{Optional: true, Sensitive: true},
"generated": schema.BoolAttribute{Optional: true},
},
},
},
"image": schema.SingleNestedAttribute{
Description: "Service details for `image` type services.",
Optional: true,
Attributes: map[string]schema.Attribute{
"image_path": schema.StringAttribute{Required: true},
"owner_id": schema.StringAttribute{Required: true},
"registry_credential_id": schema.StringAttribute{Optional: true},
},
},

"web_service_details": schema.SingleNestedAttribute{
Description: "Service details for `web_service` type services.",
Expand All @@ -96,6 +125,8 @@ func (r *serviceResource) Schema(_ context.Context, req resource.SchemaRequest,
"start_command": schema.StringAttribute{Optional: true},
},
},
"disk": disk,
"docker": docker,
},
},

Expand Down
4 changes: 2 additions & 2 deletions render/resources/service_environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,11 @@ func (r *serviceEnvironmentResource) Delete(ctx context.Context, req resource.De
response, err := r.client.UpdateEnvVarsForServiceWithResponse(ctx, state.Service.ValueString(), variables)

if err != nil {
resp.Diagnostics.AddError("failed to update service variables", err.Error())
resp.Diagnostics.AddError("failed to update service variables1", err.Error())
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
resp.Diagnostics.AddError("failed to update service variables1", err.Error())
resp.Diagnostics.AddError("failed to update service variables", err.Error())

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this a mistake?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi! Is this the only blocking point for this PR?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah

return
}

if response.StatusCode() != http.StatusCreated {
if response.StatusCode() != http.StatusOK {
resp.Diagnostics.AddError("failed to update service variables", string(response.Body))
return
}
Expand Down