Skip to content

Commit

Permalink
Add support to define multiple certificates using the same issuer (#37)
Browse files Browse the repository at this point in the history
* Add support to define multiple certificates using the same issuer

* Fix make install on arm machines
  • Loading branch information
wpjunior authored Dec 10, 2024
1 parent b4b2de8 commit aaa1a35
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ signs:
- "${signature}"
- "--detach-sign"
- "${artifact}"
release:
release: {}
# If you want to manually examine the release before its live, uncomment this line:
# draft: true
changelog:
Expand Down
27 changes: 26 additions & 1 deletion GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,32 @@ NAMESPACE=tsuru
NAME=rpaas
BINARY=terraform-provider-${NAME}
VERSION=$(shell git describe --tags $(git rev-list --tags --max-count=1) | tr -d v)
OS_ARCH=$(shell go env GOOS)_$(shell go env GOARCH)
UNAME_S := $(shell uname -s)
UNAME_P := $(shell uname -p)
ifeq ($(UNAME_S),Linux)
OS := linux
UNAME_P := $(shell uname -m)
endif
ifeq ($(UNAME_S),Darwin)
OS := darwin
UNAME_P := $(shell uname -m)
endif

ifeq ($(UNAME_P),x86_64)
ARCH := amd64
endif

ifneq ($(filter %86,$(UNAME_P)),)
ARCH := 386
endif
ifneq ($(filter arm%,$(UNAME_P)),)
ARCH := arm
endif
ifeq ($(UNAME_P),arm64)
ARCH := arm64
endif

OS_ARCH=${OS}_${ARCH}

TFPLUGINDOCS_VERSION ?= v0.16.0

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
github.com/hashicorp/terraform-plugin-sdk/v2 v2.27.0
github.com/stretchr/testify v1.8.4
github.com/tsuru/go-tsuruclient v0.0.0-20240403182619-fe8da980483b
github.com/tsuru/rpaas-operator v0.42.4
github.com/tsuru/rpaas-operator v0.43.0
k8s.io/apimachinery v0.26.2
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -759,8 +759,8 @@ github.com/tsuru/go-tsuruclient v0.0.0-20240403182619-fe8da980483b h1:CWioMVdmtk
github.com/tsuru/go-tsuruclient v0.0.0-20240403182619-fe8da980483b/go.mod h1:qwh/KJ6ypa2GISRI79XFOHhnSjGOe1cZVPHF3nfrf18=
github.com/tsuru/nginx-operator v0.15.2-0.20240515194244-a38b4b58e866 h1:aCoSpcfQuMztS/7xFrQ2ml02NxqipXYLMgJonyux6fk=
github.com/tsuru/nginx-operator v0.15.2-0.20240515194244-a38b4b58e866/go.mod h1:qdJQVY4buUQymyhcpYO99ZCfLMPRvcB/1ywK3PAh/+Q=
github.com/tsuru/rpaas-operator v0.42.4 h1:txpxiYRDYGWokPzOwfCxWk2dX0N3OVWOZ1ltGi4ICKU=
github.com/tsuru/rpaas-operator v0.42.4/go.mod h1:dxlwKGKICcstTwH0sbrrsb9Shsl2/BdDVo7uh1FWWRQ=
github.com/tsuru/rpaas-operator v0.43.0 h1:QLeNUiDFIrHeVwVlzmVieWoBJfAGgC/9DT1rDuafRl8=
github.com/tsuru/rpaas-operator v0.43.0/go.mod h1:dxlwKGKICcstTwH0sbrrsb9Shsl2/BdDVo7uh1FWWRQ=
github.com/tsuru/stern v1.20.2-0.20210928180051-1157b938dc3f h1:9dTZI6bQUVkKAsCnqaDl4V7fKAc5ErVpmX+bzevz3Cg=
github.com/tsuru/stern v1.20.2-0.20210928180051-1157b938dc3f/go.mod h1:SUQKJ3CLsVARBwkz5z9IIL8Pj008JNj4U8eF50kkm2c=
github.com/tsuru/tablecli v0.0.0-20190131152944-7ded8a3383c6 h1:1XDdWFAjIbCSG1OjN9v9KdWhuM8UtYlFcfHe/Ldkchk=
Expand Down
2 changes: 1 addition & 1 deletion internal/provider/resource_rpaas_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func resourceRpaasBlockRead(ctx context.Context, d *schema.ResourceData, meta in

var blocks []rpaastypes.Block

rpaasRetry(ctx, d.Timeout(schema.TimeoutRead), func() (*http.Response, error) {
err = rpaasRetry(ctx, d.Timeout(schema.TimeoutRead), func() (*http.Response, error) {
bs, nerr := rpaasClient.ListBlocks(ctx, rpaas_client.ListBlocksArgs{Instance: instance})
if nerr != nil {
return nil, nerr
Expand Down
94 changes: 67 additions & 27 deletions internal/provider/resource_rpaas_cert_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ func resourceRpaasCertManager() *schema.Resource {
ForceNew: true,
Description: "Certificate issuer name",
},
"certificate_name": {
Type: schema.TypeString,
ForceNew: true,
Optional: true,
Description: "Certificate Name, required on new version of RPaaS API",
},
"dns_names": {
Type: schema.TypeList,
Elem: &schema.Schema{
Expand All @@ -68,20 +74,26 @@ func resourceRpaasCertManagerCreate(ctx context.Context, d *schema.ResourceData,
return diag.Errorf("Unable to create client for service %s: %v", serviceName, err)
}

instance, issuer, dnsNames := d.Get("instance").(string), d.Get("issuer").(string), asSliceOfStrings(d.Get("dns_names"))
instance := d.Get("instance").(string)
issuer := d.Get("issuer").(string)
certificateName := d.Get("certificate_name").(string)

dnsNames := asSliceOfStrings(d.Get("dns_names"))

tflog.Info(ctx, "Create rpaas_cert_manager", map[string]interface{}{
"service": serviceName,
"instance": instance,
"issuer": issuer,
"dnsNames": dnsNames,
"certificate_name": certificateName,
"service": serviceName,
"instance": instance,
"issuer": issuer,
"dnsNames": dnsNames,
})

err = rpaasRetry(ctx, d.Timeout(schema.TimeoutCreate), func() (*http.Response, error) {
// UpdateCertManager is really an upsert
return nil, rpaasClient.UpdateCertManager(ctx, rpaas_client.UpdateCertManagerArgs{
Instance: instance,
CertManager: types.CertManager{
Name: certificateName,
Issuer: issuer,
DNSNames: dnsNames,
},
Expand All @@ -92,22 +104,35 @@ func resourceRpaasCertManagerCreate(ctx context.Context, d *schema.ResourceData,
return diag.Errorf("could not create Cert Manager request: %v", err)
}

d.SetId(fmt.Sprintf("%s::%s::%s", serviceName, instance, issuer))
var id string
if certificateName == "" {
id = fmt.Sprintf("%s::%s::%s", serviceName, instance, issuer)
} else {
id = fmt.Sprintf("%s::%s::%s::%s", serviceName, instance, issuer, certificateName)
}

d.SetId(id)
return resourceRpaasCertManagerRead(ctx, d, meta)
}

func resourceRpaasCertManagerRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
provider := meta.(*rpaasProvider)

serviceName, instance, issuer, err := parseCertManagerID(d.Id())
serviceName, instance, issuer, certificateName, err := parseCertManagerID(d.Id())
if err != nil {
return diag.Errorf("Unable to parse CertManager ID: %v", err)
}

d.SetId(fmt.Sprintf("%s::%s::%s", serviceName, instance, issuer))
var id string
if certificateName == "" {
id = fmt.Sprintf("%s::%s::%s", serviceName, instance, issuer)
} else {
id = fmt.Sprintf("%s::%s::%s::%s", serviceName, instance, issuer, certificateName)
}
d.SetId(id)
d.Set("service_name", serviceName)
d.Set("instance", instance)
d.Set("issuer", issuer)
d.Set("certificate_name", certificateName)

rpaasClient, err := provider.RpaasClient.SetService(serviceName)
if err != nil {
Expand All @@ -116,7 +141,7 @@ func resourceRpaasCertManagerRead(ctx context.Context, d *schema.ResourceData, m

var requests []types.CertManager

rpaasRetry(ctx, d.Timeout(schema.TimeoutRead), func() (*http.Response, error) {
err = rpaasRetry(ctx, d.Timeout(schema.TimeoutRead), func() (*http.Response, error) {
r, nerr := rpaasClient.ListCertManagerRequests(ctx, instance)
if nerr != nil {
return nil, nerr
Expand All @@ -130,7 +155,7 @@ func resourceRpaasCertManagerRead(ctx context.Context, d *schema.ResourceData, m
return diag.Errorf("could not list Cert Manager requests: %v", err)
}

request, found := findCertManagerRequestByIssuer(requests, issuer)
request, found := findCertManagerRequestByIssuerAndName(requests, issuer, certificateName)
if !found {
log.Printf("[DEBUG] Removing resource (ID: %s) from state as it's not found on RPaaS", d.Id())
d.SetId("")
Expand All @@ -145,7 +170,7 @@ func resourceRpaasCertManagerRead(ctx context.Context, d *schema.ResourceData, m
func resourceRpaasCertManagerUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
provider := meta.(*rpaasProvider)

serviceName, instance, issuer, err := parseCertManagerID(d.Id())
serviceName, instance, issuer, certificateName, err := parseCertManagerID(d.Id())
if err != nil {
return diag.Errorf("Unable to parse CertManager ID: %v", err)
}
Expand All @@ -157,16 +182,18 @@ func resourceRpaasCertManagerUpdate(ctx context.Context, d *schema.ResourceData,
dnsNames := asSliceOfStrings(d.Get("dns_names"))

tflog.Info(ctx, "Update rpaas_cert_manager", map[string]interface{}{
"service": serviceName,
"instance": instance,
"issuer": issuer,
"dnsNames": dnsNames,
"certificate_name": certificateName,
"service": serviceName,
"instance": instance,
"issuer": issuer,
"dnsNames": dnsNames,
})

err = rpaasRetry(ctx, d.Timeout(schema.TimeoutUpdate), func() (*http.Response, error) {
return nil, rpaasClient.UpdateCertManager(ctx, rpaas_client.UpdateCertManagerArgs{
Instance: instance,
CertManager: types.CertManager{
Name: certificateName,
Issuer: issuer,
DNSNames: dnsNames,
},
Expand All @@ -180,25 +207,32 @@ func resourceRpaasCertManagerUpdate(ctx context.Context, d *schema.ResourceData,
}

func resourceRpaasCertManagerDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
serviceName, instance, issuer, certificateName, err := parseCertManagerID(d.Id())
if err != nil {
return diag.Errorf("Unable to parse CertManager ID: %v", err)
}

provider := meta.(*rpaasProvider)

serviceName := d.Get("service_name").(string)
rpaasClient, err := provider.RpaasClient.SetService(serviceName)
if err != nil {
return diag.Errorf("Unable to create client for service %s: %v", serviceName, err)
}

instance, issuer := d.Get("instance").(string), d.Get("issuer").(string)

tflog.Info(ctx, "Delete rpaas_cert_manager", map[string]interface{}{
"service": serviceName,
"instance": instance,
"issuer": issuer,
"certificate_name": certificateName,
"service": serviceName,
"instance": instance,
"issuer": issuer,
})

err = rpaasRetry(ctx, d.Timeout(schema.TimeoutDelete), func() (*http.Response, error) {
log.Printf("[DEBUG] Removing Cert Manager certificate request: {Service: %s, Instance: %s, Issuer: %s}", serviceName, instance, issuer)
return nil, rpaasClient.DeleteCertManager(ctx, instance, issuer)

if certificateName != "" {
return nil, rpaasClient.DeleteCertManagerByName(ctx, instance, certificateName)
}
return nil, rpaasClient.DeleteCertManagerByIssuer(ctx, instance, issuer)
})

if err != nil {
Expand All @@ -208,9 +242,12 @@ func resourceRpaasCertManagerDelete(ctx context.Context, d *schema.ResourceData,
return nil
}

func findCertManagerRequestByIssuer(reqs []types.CertManager, issuer string) (*types.CertManager, bool) {
func findCertManagerRequestByIssuerAndName(reqs []types.CertManager, issuer, name string) (*types.CertManager, bool) {
for _, r := range reqs {
if r.Issuer == issuer {
if name != "" && r.Name != name {
continue
}
return &r, true
}
}
Expand All @@ -227,20 +264,23 @@ func asSliceOfStrings(data interface{}) []string {
return values
}

func parseCertManagerID(id string) (serviceName, instance, issuer string, err error) {
func parseCertManagerID(id string) (serviceName, instance, issuer, name string, err error) {
splitID := strings.Split(id, "::")

if len(splitID) != 3 {
if len(splitID) > 4 || len(splitID) < 3 {
serviceName, instance, issuer, err = parseCertManagerID_legacyV0(id)
if err != nil {
err = fmt.Errorf("Could not parse id %q. Format should be \"service::instance::issuer\"", id)
err = fmt.Errorf("Could not parse id %q. Format should be \"service::instance::issuer::certificateName\"", id)
}
return
}

serviceName = splitID[0]
instance = splitID[1]
issuer = splitID[2]
if len(splitID) == 4 {
name = splitID[3] // blank on older versions
}
return
}

Expand Down
79 changes: 77 additions & 2 deletions internal/provider/resource_rpaas_cert_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestAccRpaasCertManager_basic(t *testing.T) {
certManagers, err := testAPIClient.ListCertManagerRequests(context.Background(), "my-rpaas")
assert.NoError(t, err)
assert.Len(t, certManagers, 1)
certManager, found := findCertManagerRequestByIssuer(certManagers, "my-custom-issuer")
certManager, found := findCertManagerRequestByIssuerAndName(certManagers, "my-custom-issuer", "")
assert.True(t, found)
assert.Equal(t, "my-custom-issuer", certManager.Issuer)
assert.EqualValues(t, []string{"*.example.com", "my-instance.test"}, certManager.DNSNames)
Expand All @@ -65,7 +65,7 @@ func TestAccRpaasCertManager_basic(t *testing.T) {
certManagers, err := testAPIClient.ListCertManagerRequests(context.Background(), "my-rpaas")
assert.NoError(t, err)
assert.Len(t, certManagers, 1)
certManager, found := findCertManagerRequestByIssuer(certManagers, "my-custom-issuer")
certManager, found := findCertManagerRequestByIssuerAndName(certManagers, "my-custom-issuer", "")
assert.True(t, found)
assert.Equal(t, "my-custom-issuer", certManager.Issuer)
assert.EqualValues(t, []string{"my-instance.test"}, certManager.DNSNames)
Expand All @@ -76,6 +76,70 @@ func TestAccRpaasCertManager_basic(t *testing.T) {
})
}

func TestAccRpaasCertManager_withName(t *testing.T) {
testAPIClient, testAPIServer := setupTestAPIServer(t)
defer testAPIServer.Stop()

resourceName := "rpaas_cert_manager.cert-manager-custom-issuer"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: resourceName,
ProviderFactories: testAccProviderFactories,
CheckDestroy: nil,
Steps: []resource.TestStep{
{
Config: testAccRpaasCertManagerConfigWithName("my-custom-issuer", "example.com", `["*.example.com", "my-instance.test"]`),
Check: resource.ComposeAggregateTestCheckFunc(
testAccResourceExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "id", "rpaasv2::my-rpaas::my-custom-issuer::example.com"),
resource.TestCheckResourceAttr(resourceName, "service_name", "rpaasv2"),
resource.TestCheckResourceAttr(resourceName, "instance", "my-rpaas"),
resource.TestCheckResourceAttr(resourceName, "issuer", "my-custom-issuer"),
resource.TestCheckResourceAttr(resourceName, "certificate_name", "example.com"),
resource.TestCheckResourceAttr(resourceName, "dns_names.#", "2"),
resource.TestCheckResourceAttr(resourceName, "dns_names.0", "*.example.com"),
resource.TestCheckResourceAttr(resourceName, "dns_names.1", "my-instance.test"),
func(s *terraform.State) error {
certManagers, err := testAPIClient.ListCertManagerRequests(context.Background(), "my-rpaas")
assert.NoError(t, err)
assert.Len(t, certManagers, 1)
certManager, found := findCertManagerRequestByIssuerAndName(certManagers, "my-custom-issuer", "")
assert.True(t, found)
assert.Equal(t, "example.com", certManager.Name)
assert.Equal(t, "my-custom-issuer", certManager.Issuer)
assert.EqualValues(t, []string{"*.example.com", "my-instance.test"}, certManager.DNSNames)
return nil
},
),
},
{
// Testing Update
Config: testAccRpaasCertManagerConfigWithName("my-custom-issuer", "example.com", `["my-instance.test"]`),
Check: resource.ComposeAggregateTestCheckFunc(
testAccResourceExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "id", "rpaasv2::my-rpaas::my-custom-issuer::example.com"),
resource.TestCheckResourceAttr(resourceName, "service_name", "rpaasv2"),
resource.TestCheckResourceAttr(resourceName, "instance", "my-rpaas"),
resource.TestCheckResourceAttr(resourceName, "issuer", "my-custom-issuer"),
resource.TestCheckResourceAttr(resourceName, "certificate_name", "example.com"),
resource.TestCheckResourceAttr(resourceName, "dns_names.#", "1"),
resource.TestCheckResourceAttr(resourceName, "dns_names.0", "my-instance.test"),
func(s *terraform.State) error {
certManagers, err := testAPIClient.ListCertManagerRequests(context.Background(), "my-rpaas")
assert.NoError(t, err)
assert.Len(t, certManagers, 1)
certManager, found := findCertManagerRequestByIssuerAndName(certManagers, "my-custom-issuer", "")
assert.True(t, found)
assert.Equal(t, "my-custom-issuer", certManager.Issuer)
assert.Equal(t, "example.com", certManager.Name)
assert.EqualValues(t, []string{"my-instance.test"}, certManager.DNSNames)
return nil
}),
},
},
})
}

func TestAccRpaasCertManager_import(t *testing.T) {
testAPIClient, testAPIServer := setupTestAPIServer(t)
defer testAPIServer.Stop()
Expand Down Expand Up @@ -142,3 +206,14 @@ resource "rpaas_cert_manager" "cert-manager-custom-issuer" {
dns_names = %s
}`, issuer, dnsNamesArray)
}

func testAccRpaasCertManagerConfigWithName(issuer, name, dnsNamesArray string) string {
return fmt.Sprintf(`
resource "rpaas_cert_manager" "cert-manager-custom-issuer" {
instance = "my-rpaas"
service_name = "rpaasv2"
certificate_name = "%s"
issuer = "%s"
dns_names = %s
}`, name, issuer, dnsNamesArray)
}
Loading

0 comments on commit aaa1a35

Please sign in to comment.