Skip to content

Commit

Permalink
feat(COR-963): Helm - allow custom domain setup (#411)
Browse files Browse the repository at this point in the history
  • Loading branch information
pggb25 authored Jul 17, 2024
1 parent 554cd1b commit 8d1732d
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 6 deletions.
16 changes: 16 additions & 0 deletions docs/data-sources/helm.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ data "qovery_helm" "my_helm" {
- `arguments` (Set of String) Helm arguments
- `auto_deploy` (Boolean) Specify the service will be automatically updated on every new commit on the branch.
- `auto_preview` (Boolean) Specify if the environment preview option is activated or not for this helm.
- `custom_domains` (Attributes Set) List of custom domains linked to this container. (see [below for nested schema](#nestedatt--custom_domains))
- `deployment_restrictions` (Attributes Set) List of deployment restrictions (see [below for nested schema](#nestedatt--deployment_restrictions))
- `deployment_stage_id` (String) Id of the deployment stage.
- `environment_variable_aliases` (Attributes Set) List of environment variable aliases linked to this helm. (see [below for nested schema](#nestedatt--environment_variable_aliases))
Expand All @@ -43,6 +44,21 @@ data "qovery_helm" "my_helm" {
- `source` (Attributes) Helm chart from a Helm repository or from a git repository (see [below for nested schema](#nestedatt--source))
- `values_override` (Attributes) Define your own overrides to customize the helm chart behaviour. (see [below for nested schema](#nestedatt--values_override))

<a id="nestedatt--custom_domains"></a>
### Nested Schema for `custom_domains`

Optional:

- `generate_certificate` (Boolean) Qovery will generate and manage the certificate for this domain.

Read-Only:

- `domain` (String) Your custom domain.
- `id` (String) Id of the custom domain.
- `status` (String) Status of the custom domain.
- `validation_domain` (String) URL provided by Qovery. You must create a CNAME on your DNS provider using that URL.


<a id="nestedatt--deployment_restrictions"></a>
### Nested Schema for `deployment_restrictions`

Expand Down
16 changes: 16 additions & 0 deletions docs/resources/helm.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ resource "qovery_helm" "my_helm" {
- `arguments` (Set of String) Helm arguments
- `auto_deploy` (Boolean) Specify if service will be automatically updated on every new commit on the branch.
- `auto_preview` (Boolean) Specify if the environment preview option is activated or not for this helm.
- `custom_domains` (Attributes Set) List of custom domains linked to this helm. (see [below for nested schema](#nestedatt--custom_domains))
- `deployment_restrictions` (Attributes Set) List of deployment restrictions (see [below for nested schema](#nestedatt--deployment_restrictions))
- `deployment_stage_id` (String) Id of the deployment stage.
- `environment_variable_aliases` (Attributes Set) List of environment variable aliases linked to this helm. (see [below for nested schema](#nestedatt--environment_variable_aliases))
Expand Down Expand Up @@ -223,6 +224,21 @@ Required:



<a id="nestedatt--custom_domains"></a>
### Nested Schema for `custom_domains`

Required:

- `domain` (String) Your custom domain.
- `generate_certificate` (Boolean) Qovery will generate and manage the certificate for this domain.

Read-Only:

- `id` (String) Id of the custom domain.
- `status` (String) Status of the custom domain.
- `validation_domain` (String) URL provided by Qovery. You must create a CNAME on your DNS provider using that URL.


<a id="nestedatt--deployment_restrictions"></a>
### Nested Schema for `deployment_restrictions`

Expand Down
1 change: 1 addition & 0 deletions internal/domain/apierrors/api_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const (
APIResourceHelmSecret APIResource = "helm secret"
APIResourceHelmStatus APIResource = "helm status"
APIResourceHelmRepository APIResource = "helm repository"
APIResourceHelmCustomDomain APIResource = "helm custom domain"
APIResourceAnnotationsGroup APIResource = "annotations group"
APIResourceLabelsGroup APIResource = "labels group"
)
3 changes: 3 additions & 0 deletions internal/domain/helm/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type Helm struct {
DeploymentStageID string
AdvancedSettingsJson string
JobDeploymentRestrictions []deploymentrestriction.ServiceDeploymentRestriction
CustomDomains []*qovery.CustomDomain
}

type SourceResponse struct {
Expand Down Expand Up @@ -108,6 +109,7 @@ type NewHelmParams struct {
Secrets secret.NewSecretsParams
DeploymentStageID string
AdvancedSettingsJson string
CustomDomains []*qovery.CustomDomain
}

func NewHelm(params NewHelmParams) (*Helm, error) {
Expand Down Expand Up @@ -163,6 +165,7 @@ func NewHelm(params NewHelmParams) (*Helm, error) {
Ports: ports,
DeploymentStageID: params.DeploymentStageID,
AdvancedSettingsJson: params.AdvancedSettingsJson,
CustomDomains: params.CustomDomains,
}

environmentVariables := make(variable.Variables, len(params.EnvironmentVariables))
Expand Down
2 changes: 2 additions & 0 deletions internal/domain/helm/helm_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package helm
import (
"context"
"github.com/qovery/qovery-client-go"
"github.com/qovery/terraform-provider-qovery/client"

"github.com/go-playground/validator/v10"
"github.com/pkg/errors"
Expand Down Expand Up @@ -33,6 +34,7 @@ type UpsertRepositoryRequest struct {
Secrets []secret.UpsertRequest
DeploymentStageID string
AdvancedSettingsJson string
CustomDomains client.CustomDomainsDiff
}

func (r UpsertRepositoryRequest) Validate() error {
Expand Down
78 changes: 75 additions & 3 deletions internal/infrastructure/repositories/qoveryapi/helm_qoveryapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,23 @@ func (c helmQoveryAPI) Create(ctx context.Context, environmentID string, request
return nil, apierrors.NewCreateAPIError(apierrors.APIResourceHelm, request.Name, resp, err)
}

// Create custom domains
if !request.CustomDomains.IsEmpty() {
for _, customDomain := range request.CustomDomains.Create {
_, resp, err := c.client.HelmCustomDomainAPI.
CreateHelmCustomDomain(ctx, newHelm.Id).
CustomDomainRequest(
qovery.CustomDomainRequest{
Domain: customDomain.Domain,
GenerateCertificate: customDomain.GenerateCertificate,
}).
Execute()
if err != nil || resp.StatusCode >= 400 {
return nil, apierrors.NewCreateAPIError(apierrors.APIResourceHelmCustomDomain, request.Name, resp, err)
}
}
}

// Attach helm to deployment stage
if len(request.DeploymentStageID) > 0 {
_, response, err := c.client.DeploymentStageMainCallsAPI.AttachServiceToDeploymentStage(ctx, request.DeploymentStageID, newHelm.Id).Execute()
Expand All @@ -67,7 +84,13 @@ func (c helmQoveryAPI) Create(ctx context.Context, environmentID string, request
return nil, apierrors.NewCreateAPIError(apierrors.APIResourceHelm, newHelm.Id, resp, err)
}

return newDomainHelmFromQovery(newHelm, deploymentStage.Id, request.AdvancedSettingsJson)
// Get custom domains
customDomains, _, err := c.client.HelmCustomDomainAPI.ListHelmCustomDomain(ctx, newHelm.Id).Execute()
if err != nil || resp.StatusCode >= 400 {
return nil, apierrors.NewCreateAPIError(apierrors.APIResourceHelmCustomDomain, newHelm.Id, resp, err)
}

return newDomainHelmFromQovery(newHelm, deploymentStage.Id, request.AdvancedSettingsJson, customDomains)
}

// Get calls Qovery's API to retrieve a helm using the given helmID.
Expand All @@ -90,7 +113,13 @@ func (c helmQoveryAPI) Get(ctx context.Context, helmID string, advancedSettingsJ
return nil, apierrors.NewReadAPIError(apierrors.APIResourceHelm, helmID, nil, err)
}

return newDomainHelmFromQovery(helm, deploymentStage.Id, *advancedSettingsAsJson)
// Get custom domains
customDomains, _, err := c.client.HelmCustomDomainAPI.ListHelmCustomDomain(ctx, helm.Id).Execute()
if err != nil || resp.StatusCode >= 400 {
return nil, apierrors.NewCreateAPIError(apierrors.APIResourceHelmCustomDomain, helm.Id, resp, err)
}

return newDomainHelmFromQovery(helm, deploymentStage.Id, *advancedSettingsAsJson, customDomains)
}

// Update calls Qovery's API to update a helm using the given helmID and request.
Expand All @@ -108,6 +137,43 @@ func (c helmQoveryAPI) Update(ctx context.Context, helmID string, request helm.U
return nil, apierrors.NewUpdateAPIError(apierrors.APIResourceHelm, helmID, resp, err)
}

// Create custom domains
if !request.CustomDomains.IsEmpty() {
for _, customDomain := range request.CustomDomains.Delete {
_, err := c.client.HelmCustomDomainAPI.
DeleteHelmCustomDomain(ctx, helmID, customDomain.Id).Execute()
if err != nil || resp.StatusCode >= 400 {
return nil, apierrors.NewCreateAPIError(apierrors.APIResourceHelmCustomDomain, request.Name, resp, err)
}
}
for _, customDomain := range request.CustomDomains.Update {
_, resp, err := c.client.HelmCustomDomainAPI.
EditHelmCustomDomain(ctx, helmID, customDomain.Id).
CustomDomainRequest(
qovery.CustomDomainRequest{
Domain: customDomain.Domain,
GenerateCertificate: customDomain.GenerateCertificate,
}).
Execute()
if err != nil || resp.StatusCode >= 400 {
return nil, apierrors.NewCreateAPIError(apierrors.APIResourceHelmCustomDomain, request.Name, resp, err)
}
}
for _, customDomain := range request.CustomDomains.Create {
_, resp, err := c.client.HelmCustomDomainAPI.
CreateHelmCustomDomain(ctx, helmID).
CustomDomainRequest(
qovery.CustomDomainRequest{
Domain: customDomain.Domain,
GenerateCertificate: customDomain.GenerateCertificate,
}).
Execute()
if err != nil || resp.StatusCode >= 400 {
return nil, apierrors.NewCreateAPIError(apierrors.APIResourceHelmCustomDomain, request.Name, resp, err)
}
}
}

// Attach helm to deployment stage
if len(request.DeploymentStageID) > 0 {
_, response, err := c.client.DeploymentStageMainCallsAPI.AttachServiceToDeploymentStage(ctx, request.DeploymentStageID, helmID).Execute()
Expand All @@ -128,7 +194,13 @@ func (c helmQoveryAPI) Update(ctx context.Context, helmID string, request helm.U
return nil, apierrors.NewCreateAPIError(apierrors.APIResourceHelm, helmID, resp, err)
}

return newDomainHelmFromQovery(helm, deploymentStage.Id, request.AdvancedSettingsJson)
// Get custom domains
customDomains, _, err := c.client.HelmCustomDomainAPI.ListHelmCustomDomain(ctx, helm.Id).Execute()
if err != nil || resp.StatusCode >= 400 {
return nil, apierrors.NewCreateAPIError(apierrors.APIResourceHelmCustomDomain, helm.Id, resp, err)
}

return newDomainHelmFromQovery(helm, deploymentStage.Id, request.AdvancedSettingsJson, customDomains)
}

// Delete calls Qovery's API to deletes a helm using the given helmID.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func getAggregateHelmResponse(helmResponse *qovery.HelmResponse) AggregateHelmRe
}

// newDomainCredentialsFromQovery takes a qovery.EnvironmentVariable returned by the API client and turns it into the domain model variable.Variable.
func newDomainHelmFromQovery(helmResponse *qovery.HelmResponse, deploymentStageID string, advancedSettingsJson string) (*helm.Helm, error) {
func newDomainHelmFromQovery(helmResponse *qovery.HelmResponse, deploymentStageID string, advancedSettingsJson string, qoveryCustomDomains *qovery.CustomDomainResponseList) (*helm.Helm, error) {
if helmResponse == nil {
return nil, variable.ErrNilVariable
}
Expand Down Expand Up @@ -157,6 +157,12 @@ func newDomainHelmFromQovery(helmResponse *qovery.HelmResponse, deploymentStageI
}
}

customDomains := make([]*qovery.CustomDomain, 0, len(qoveryCustomDomains.GetResults()))
for _, v := range qoveryCustomDomains.GetResults() {
cpy := v
customDomains = append(customDomains, &cpy)
}

return helm.NewHelm(helm.NewHelmParams{
HelmID: h.Id,
EnvironmentID: h.EnvironmentId,
Expand All @@ -171,6 +177,7 @@ func newDomainHelmFromQovery(helmResponse *qovery.HelmResponse, deploymentStageI
Ports: ports,
DeploymentStageID: deploymentStageID,
AdvancedSettingsJson: advancedSettingsJson,
CustomDomains: customDomains,
})
}

Expand Down
28 changes: 28 additions & 0 deletions qovery/data_source_helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,34 @@ func (d helmDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, re
},
},
},
"custom_domains": schema.SetNestedAttribute{
Description: "List of custom domains linked to this container.",
Optional: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "Id of the custom domain.",
Computed: true,
},
"domain": schema.StringAttribute{
Description: "Your custom domain.",
Computed: true,
},
"validation_domain": schema.StringAttribute{
Description: "URL provided by Qovery. You must create a CNAME on your DNS provider using that URL.",
Computed: true,
},
"generate_certificate": schema.BoolAttribute{
Description: "Qovery will generate and manage the certificate for this domain.",
Optional: true,
},
"status": schema.StringAttribute{
Description: "Status of the custom domain.",
Computed: true,
},
},
},
},
},
}
}
Expand Down
28 changes: 28 additions & 0 deletions qovery/resource_helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,34 @@ func (r helmResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *
},
},
},
"custom_domains": schema.SetNestedAttribute{
Description: "List of custom domains linked to this helm.",
Optional: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "Id of the custom domain.",
Computed: true,
},
"domain": schema.StringAttribute{
Description: "Your custom domain.",
Required: true,
},
"generate_certificate": schema.BoolAttribute{
Description: "Qovery will generate and manage the certificate for this domain.",
Required: true,
},
"validation_domain": schema.StringAttribute{
Description: "URL provided by Qovery. You must create a CNAME on your DNS provider using that URL.",
Computed: true,
},
"status": schema.StringAttribute{
Description: "Status of the custom domain.",
Computed: true,
},
},
},
},
"external_host": schema.StringAttribute{
Description: "The helm external FQDN host [NOTE: only if your helm is using a publicly accessible port].",
Computed: true,
Expand Down
14 changes: 12 additions & 2 deletions qovery/resource_helm_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package qovery
import (
"context"
"fmt"
"github.com/qovery/terraform-provider-qovery/client"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
Expand Down Expand Up @@ -39,6 +40,7 @@ type Helm struct {
DeploymentStageId types.String `tfsdk:"deployment_stage_id"`
AdvancedSettingsJson types.String `tfsdk:"advanced_settings_json"`
DeploymentRestrictions types.Set `tfsdk:"deployment_restrictions"`
CustomDomains types.Set `tfsdk:"custom_domains"`
}

type HelmSource struct {
Expand Down Expand Up @@ -119,6 +121,10 @@ func (j Helm) DeploymentRestrictionDiff(deploymentRestrictionsState *types.Set)
return deploymentrestriction.ToDeploymentRestrictionDiff(j.DeploymentRestrictions, deploymentRestrictionsState)
}

func (h Helm) CustomDomainsList() CustomDomainList {
return toCustomDomainList(h.CustomDomains)
}

func (h Helm) toUpsertServiceRequest(state *Helm) (*helm.UpsertServiceRequest, error) {
var stateEnvironmentVariables EnvironmentVariableList
var stateEnvironmentVariableAliases EnvironmentVariableList
Expand All @@ -127,6 +133,7 @@ func (h Helm) toUpsertServiceRequest(state *Helm) (*helm.UpsertServiceRequest, e
var stateSecretAliases SecretList
var stateSecretOverrides SecretList
var stateDeploymentRestrictions types.Set
var stateCustomDomains CustomDomainList

if state != nil {
stateEnvironmentVariables = state.EnvironmentVariableList()
Expand All @@ -136,9 +143,10 @@ func (h Helm) toUpsertServiceRequest(state *Helm) (*helm.UpsertServiceRequest, e
stateSecretAliases = state.SecretAliasesList()
stateSecretOverrides = state.SecretOverridesList()
stateDeploymentRestrictions = state.DeploymentRestrictions
stateCustomDomains = state.CustomDomainsList()
}

helmRequest, err := h.toUpsertRepositoryRequest()
helmRequest, err := h.toUpsertRepositoryRequest(h.CustomDomainsList().diff(stateCustomDomains))
if err != nil {
return nil, err
}
Expand All @@ -160,7 +168,7 @@ func (h Helm) toUpsertServiceRequest(state *Helm) (*helm.UpsertServiceRequest, e
}, nil
}

func (h Helm) toUpsertRepositoryRequest() (*helm.UpsertRepositoryRequest, error) {
func (h Helm) toUpsertRepositoryRequest(customDomainsDiff client.CustomDomainsDiff) (*helm.UpsertRepositoryRequest, error) {
var ports *[]helm.Port = nil
if h.Ports != nil {
portLists := make([]helm.Port, 0, len(*h.Ports))
Expand Down Expand Up @@ -197,6 +205,7 @@ func (h Helm) toUpsertRepositoryRequest() (*helm.UpsertRepositoryRequest, error)
DeploymentStageID: ToString(h.DeploymentStageId),
Ports: ports,
AdvancedSettingsJson: ToString(h.AdvancedSettingsJson),
CustomDomains: customDomainsDiff,
}, nil
}

Expand Down Expand Up @@ -462,5 +471,6 @@ func convertDomainHelmToHelm(ctx context.Context, state Helm, helm *helm.Helm) H
DeploymentStageId: FromString(helm.DeploymentStageID),
AdvancedSettingsJson: FromString(helm.AdvancedSettingsJson),
DeploymentRestrictions: FromDeploymentRestrictionList(state.DeploymentRestrictions, helm.JobDeploymentRestrictions),
CustomDomains: fromCustomDomainList(state.CustomDomains, helm.CustomDomains).toTerraformSet(ctx),
}
}

0 comments on commit 8d1732d

Please sign in to comment.