Skip to content

Commit

Permalink
Merge pull request #379 from Scalingo/feat/STORY-443/domain-no-letsen…
Browse files Browse the repository at this point in the history
…crypt

[STORY-443] Add ability to create/update domain without Letsencrypt Certificate Generation
  • Loading branch information
leo-scalingo authored Apr 19, 2024
2 parents 881404b + 0f5f3c4 commit 986a75a
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 33 deletions.
68 changes: 44 additions & 24 deletions domains.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import (

type DomainsService interface {
DomainsList(ctx context.Context, app string) ([]Domain, error)
DomainsAdd(ctx context.Context, app string, d Domain) (Domain, error)
DomainsAdd(ctx context.Context, app string, d DomainsAddParams) (Domain, error)
DomainsUpdate(ctx context.Context, app string, id string, d DomainsUpdateParams) (Domain, error)
DomainsRemove(ctx context.Context, app string, id string) error
// DomainsUpdate helpers
DomainSetCanonical(ctx context.Context, app, id string) (Domain, error)
DomainUnsetCanonical(ctx context.Context, app string) (Domain, error)
DomainSetCertificate(ctx context.Context, app, id, tlsCert, tlsKey string) (Domain, error)
Expand Down Expand Up @@ -44,15 +46,20 @@ type ACMEErrorVariables struct {
}

type Domain struct {
ID string `json:"id"`
AppID string `json:"app_id"`
Name string `json:"name"`
TLSCert string `json:"tlscert,omitempty"`
TLSKey string `json:"tlskey,omitempty"`
SSL bool `json:"ssl"`
Validity time.Time `json:"validity"`
Canonical bool `json:"canonical"`
LetsEncrypt bool `json:"letsencrypt"`
ID string `json:"id"`
AppID string `json:"app_id"`
Name string `json:"name"`
TLSCert string `json:"tlscert,omitempty"`
TLSKey string `json:"tlskey,omitempty"`
// LetsEncryptEnabled is true (default) if automatic certificate generation
// is enabled
LetsEncryptEnabled bool `json:"letsencrypt_enabled"`
SSL bool `json:"ssl"`
Validity time.Time `json:"validity"`
Canonical bool `json:"canonical"`
// LetsEncrypt is true if the certificate is generated by LetsEncrypt
LetsEncrypt bool `json:"letsencrypt"`
// LetsEncryptStatus is the status of the LetsEncrypt certificate generation
LetsEncryptStatus LetsEncryptStatus `json:"letsencrypt_status"`
SslStatus SslStatus `json:"ssl_status"`
AcmeDNSFqdn string `json:"acme_dns_fqdn"`
Expand Down Expand Up @@ -84,9 +91,17 @@ func (c *Client) DomainsList(ctx context.Context, app string) ([]Domain, error)
return domainRes.Domains, nil
}

func (c *Client) DomainsAdd(ctx context.Context, app string, d Domain) (Domain, error) {
type DomainsAddParams struct {
Name string `json:"name"`
Canonical *bool `json:"canonical,omitempty"`
TLSCert *string `json:"tlscert,omitempty"`
TLSKey *string `json:"tlskey,omitempty"`
LetsEncryptEnabled *bool `json:"letsencrypt_enabled,omitempty"`
}

func (c *Client) DomainsAdd(ctx context.Context, app string, params DomainsAddParams) (Domain, error) {
var domainRes DomainRes
err := c.ScalingoAPI().SubresourceAdd(ctx, "apps", app, "domains", DomainRes{d}, &domainRes)
err := c.ScalingoAPI().SubresourceAdd(ctx, "apps", app, "domains", map[string]DomainsAddParams{"domain": params}, &domainRes)
if err != nil {
return Domain{}, errgo.Notef(err, "fail to add a domain")
}
Expand All @@ -108,38 +123,42 @@ func (c *Client) DomainsShow(ctx context.Context, app, id string) (Domain, error
return domainRes.Domain, nil
}

func (c *Client) domainsUpdate(ctx context.Context, app, id string, domain Domain) (Domain, error) {
type DomainsUpdateParams struct {
Canonical *bool `json:"canonical,omitempty"`
TLSCert *string `json:"tlscert,omitempty"`
TLSKey *string `json:"tlskey,omitempty"`
LetsEncryptEnabled *bool `json:"letsencrypt_enabled,omitempty"`
}

func (c *Client) DomainsUpdate(ctx context.Context, app, id string, params DomainsUpdateParams) (Domain, error) {
var domainRes DomainRes
err := c.ScalingoAPI().SubresourceUpdate(ctx, "apps", app, "domains", id, DomainRes{Domain: domain}, &domainRes)
err := c.ScalingoAPI().SubresourceUpdate(ctx, "apps", app, "domains", id, map[string]DomainsUpdateParams{"domain": params}, &domainRes)
if err != nil {
return Domain{}, errgo.Notef(err, "fail to update the domain")
}
return domainRes.Domain, nil
}

func (c *Client) DomainSetCertificate(ctx context.Context, app, id, tlsCert, tlsKey string) (Domain, error) {
domain, err := c.domainsUpdate(ctx, app, id, Domain{TLSCert: tlsCert, TLSKey: tlsKey})
domain, err := c.DomainsUpdate(ctx, app, id, DomainsUpdateParams{TLSCert: &tlsCert, TLSKey: &tlsKey})
if err != nil {
return Domain{}, errgo.Notef(err, "fail to set the domain certificate")
}
return domain, nil
}

func (c *Client) DomainUnsetCertificate(ctx context.Context, app, id string) (Domain, error) {
var domainRes DomainRes
err := c.ScalingoAPI().SubresourceUpdate(
ctx, "apps", app, "domains", id, map[string]domainUnsetCertificateParams{
"domain": {TLSCert: "", TLSKey: ""},
}, &domainRes,
)
empty := ""
domain, err := c.DomainsUpdate(ctx, app, id, DomainsUpdateParams{TLSCert: &empty, TLSKey: &empty})
if err != nil {
return Domain{}, errgo.Notef(err, "fail to unset the domain certificate")
}
return domainRes.Domain, nil
return domain, nil
}

func (c *Client) DomainSetCanonical(ctx context.Context, app, id string) (Domain, error) {
domain, err := c.domainsUpdate(ctx, app, id, Domain{Canonical: true})
value := true
domain, err := c.DomainsUpdate(ctx, app, id, DomainsUpdateParams{Canonical: &value})
if err != nil {
return Domain{}, errgo.Notef(err, "fail to set the domain as canonical")
}
Expand All @@ -154,7 +173,8 @@ func (c *Client) DomainUnsetCanonical(ctx context.Context, app string) (Domain,

for _, domain := range domains {
if domain.Canonical {
domain, err := c.domainsUpdate(ctx, app, domain.ID, Domain{Canonical: false})
value := false
domain, err := c.DomainsUpdate(ctx, app, domain.ID, DomainsUpdateParams{Canonical: &value})
if err != nil {
return Domain{}, errgo.Notef(err, "fail to unset the domain as canonical")
}
Expand Down
54 changes: 45 additions & 9 deletions domains_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/stretchr/testify/require"
)

func TestDomainsClient_DomainCanonical(t *testing.T) {
func TestDomainsClient_Domain_Updates(t *testing.T) {
ctx := context.Background()
appName := "my-app"
domainID := "domain-id"
Expand All @@ -27,6 +27,7 @@ func TestDomainsClient_DomainCanonical(t *testing.T) {
mockDomainsList func(t *testing.T, w http.ResponseWriter, r *http.Request)
expectedError string
}{
// Canonical status update
"it should set the domain as canonical": {
testedClientCall: func(c DomainsService) error {
_, err := c.DomainSetCanonical(ctx, appName, domainID)
Expand All @@ -38,7 +39,7 @@ func TestDomainsClient_DomainCanonical(t *testing.T) {
responseStatus: 200,
},
"it should unset the domain as canonical": {
mockDomainsList: func(t *testing.T, w http.ResponseWriter, r *http.Request) {
mockDomainsList: func(t *testing.T, w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(200)
err := json.NewEncoder(w).Encode(DomainsRes{Domains: []Domain{
{
Expand All @@ -57,6 +58,29 @@ func TestDomainsClient_DomainCanonical(t *testing.T) {
expectedParams: `"canonical":false`,
responseStatus: 200,
},
"it should return an error if there is no canonical domain": {
mockDomainsList: func(t *testing.T, w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(200)
err := json.NewEncoder(w).Encode(DomainsRes{Domains: []Domain{}})
assert.NoError(t, err)
},
testedClientCall: func(c DomainsService) error {
_, err := c.DomainUnsetCanonical(ctx, appName)
return err
},
expectedError: "no canonical domain configured",
},
// Domain certificate update
"it should set the domain certificate": {
testedClientCall: func(c DomainsService) error {
_, err := c.DomainSetCertificate(ctx, appName, domainID, "cert", "key")
return err
},
expectedEndpoint: "/v1/apps/my-app/domains/domain-id",
expectedMethod: "PATCH",
expectedParams: `"tlscert":"cert","tlskey":"key"`,
responseStatus: 200,
},
"it should unset the domain certificate": {
testedClientCall: func(c DomainsService) error {
_, err := c.DomainUnsetCertificate(ctx, appName, domainID)
Expand All @@ -67,17 +91,29 @@ func TestDomainsClient_DomainCanonical(t *testing.T) {
expectedParams: `"tlscert":"","tlskey":""`,
responseStatus: 200,
},
"it should return an error if there is no canonical domain": {
mockDomainsList: func(t *testing.T, w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
err := json.NewEncoder(w).Encode(DomainsRes{Domains: []Domain{}})
assert.NoError(t, err)
// Letsencrypt certificate generation
"it should update letsencrypt_enabled to false": {
testedClientCall: func(c DomainsService) error {
f := false
_, err := c.DomainsUpdate(ctx, appName, domainID, DomainsUpdateParams{
LetsEncryptEnabled: &f,
})
return err
},
expectedEndpoint: "/v1/apps/my-app/domains/domain-id",
expectedMethod: "PATCH",
expectedParams: `"letsencrypt_enabled":false`,
responseStatus: 200,
},
"it should not update anything if no params": {
testedClientCall: func(c DomainsService) error {
_, err := c.DomainUnsetCanonical(ctx, appName)
_, err := c.DomainsUpdate(ctx, appName, domainID, DomainsUpdateParams{})
return err
},
expectedError: "no canonical domain configured",
expectedEndpoint: "/v1/apps/my-app/domains/domain-id",
expectedMethod: "PATCH",
expectedParams: `{"domain":{}}`,
responseStatus: 200,
},
}

Expand Down

0 comments on commit 986a75a

Please sign in to comment.