Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix certificate deletion #987

Merged
merged 4 commits into from
Apr 20, 2021
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,37 @@ func TestAccLBV2Listener_tls(t *testing.T) {
})
}

func TestAccLBV2ListenerSni(t *testing.T) {
// var listener listeners.Listener
resourceName := "opentelekomcloud_lb_listener_v2.elb_listener"

resource.Test(t, resource.TestCase{
PreCheck: func() { common.TestAccPreCheck(t) },
Providers: common.TestAccProviders,
CheckDestroy: testAccCheckLBV2ListenerDestroy,
Steps: []resource.TestStep{
{
Config: testAccLBV2ListenerConfigCert(1),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "sni_container_refs.#", "1"),
),
},
{
Config: testAccLBV2ListenerConfigCert(3),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "sni_container_refs.#", "3"),
),
},
{
Config: testAccLBV2ListenerConfigCert(1),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "sni_container_refs.#", "1"),
),
},
},
})
}

func testAccCheckLBV2ListenerDestroy(s *terraform.State) error {
config := common.TestAccProvider.Meta().(*cfg.Config)
networkingClient, err := config.NetworkingV2Client(env.OS_REGION_NAME)
Expand Down Expand Up @@ -419,3 +450,86 @@ resource "opentelekomcloud_lb_listener_v2" "listener_tls" {
}
`, env.OS_SUBNET_ID)
)

func testAccLBV2ListenerConfigCert(count int) string {
return fmt.Sprintf(`
resource "opentelekomcloud_lb_loadbalancer_v2" "elb_public" {
vip_subnet_id = "%s"
}

resource "opentelekomcloud_lb_certificate_v2" "elb_certificates" {
count = %d
name = "www${count.index}.acme.com"
domain = "www${count.index}.acme.com"
private_key = <<EOT
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwZ5UJULAjWr7p6FVwGRQRjFN2s8tZ/6LC3X82fajpVsYqF1x
qEuUDndDXVD09E4u83MS6HO6a3bIVQDp6/klnYldiE6Vp8HH5BSKaCWKVg8lGWg1
UM9wZFnlryi14KgmpIFmcu9nA8yV/6MZAe6RSDmb3iyNBmiZ8aZhGw2pI1YwR+15
MVqFFGB+7ExkziROi7L8CFCyCezK2/oOOvQsH1dzQ8z1JXWdg8/9Zx7Ktvgwu5PQ
M3cJtSHX6iBPOkMU8Z8TugLlTqQXKZOEgwajwvQ5mf2DPkVgM08XAgaLJcLigwD5
13koAdtJd5v+9irw+5LAuO3JclqwTvwy7u/YwwIDAQABAoIBACU9S5fjD9/jTMXA
DRs08A+gGgZUxLn0xk+NAPX3LyB1tfdkCaFB8BccLzO6h3KZuwQOBPv6jkdvEDbx
Nwyw3eA/9GJsIvKiHc0rejdvyPymaw9I8MA7NbXHaJrY7KpqDQyk6sx+aUTcy5jg
iMXLWdwXYHhJ/1HVOo603oZyiS6HZeYU089NDUcX+1SJi3e5Ke0gPVXEqCq1O11/
rh24bMxnwZo4PKBWdcMBN5Zf/4ij9vrZE+fFzW7vGBO48A5lvZxWU2U5t/OZQRtN
1uLOHmMFa0FIF2aWbTVfwdUWAFsvAOkHj9VV8BXOUwKOUuEktdkfAlvrxmsFrO/H
yDeYYPkCgYEA/S55CBbR0sMXpSZ56uRn8JHApZJhgkgvYr+FqDlJq/e92nAzf01P
RoEBUajwrnf1ycevN/SDfbtWzq2XJGqhWdJmtpO16b7KBsC6BdRcH6dnOYh31jgA
vABMIP3wzI4zSVTyxRE8LDuboytF1mSCeV5tHYPQTZNwrplDnLQhywcCgYEAw8Yc
Uk/eiFr3hfH/ZohMfV5p82Qp7DNIGRzw8YtVG/3+vNXrAXW1VhugNhQY6L+zLtJC
aKn84ooup0m3YCg0hvINqJuvzfsuzQgtjTXyaE0cEwsjUusOmiuj09vVx/3U7siK
Hdjd2ICPCvQ6Q8tdi8jV320gMs05AtaBkZdsiWUCgYEAtLw4Kk4f+xTKDFsrLUNf
75wcqhWVBiwBp7yQ7UX4EYsJPKZcHMRTk0EEcAbpyaJZE3I44vjp5ReXIHNLMfPs
uvI34J4Rfot0LN3n7cFrAi2+wpNo+MOBwrNzpRmijGP2uKKrq4JiMjFbKV/6utGF
Up7VxfwS904JYpqGaZctiIECgYA1A6nZtF0riY6ry/uAdXpZHL8ONNqRZtWoT0kD
79otSVu5ISiRbaGcXsDExC52oKrSDAgFtbqQUiEOFg09UcXfoR6HwRkba2CiDwve
yHQLQI5Qrdxz8Mk0gIrNrSM4FAmcW9vi9z4kCbQyoC5C+4gqeUlJRpDIkQBWP2Y4
2ct/bQKBgHv8qCsQTZphOxc31BJPa2xVhuv18cEU3XLUrVfUZ/1f43JhLp7gynS2
ep++LKUi9D0VGXY8bqvfJjbECoCeu85vl8NpCXwe/LoVoIn+7KaVIZMwqoGMfgNl
nEqm7HWkNxHhf8A6En/IjleuddS1sf9e/x+TJN1Xhnt9W6pe7Fk1
-----END RSA PRIVATE KEY-----
EOT

certificate = <<EOT
-----BEGIN CERTIFICATE-----
MIIDpTCCAo2gAwIBAgIJAKdmmOBYnFvoMA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNV
BAYTAnh4MQswCQYDVQQIDAJ4eDELMAkGA1UEBwwCeHgxCzAJBgNVBAoMAnh4MQsw
CQYDVQQLDAJ4eDELMAkGA1UEAwwCeHgxGTAXBgkqhkiG9w0BCQEWCnh4QDE2My5j
b20wHhcNMTcxMjA0MDM0MjQ5WhcNMjAxMjAzMDM0MjQ5WjBpMQswCQYDVQQGEwJ4
eDELMAkGA1UECAwCeHgxCzAJBgNVBAcMAnh4MQswCQYDVQQKDAJ4eDELMAkGA1UE
CwwCeHgxCzAJBgNVBAMMAnh4MRkwFwYJKoZIhvcNAQkBFgp4eEAxNjMuY29tMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwZ5UJULAjWr7p6FVwGRQRjFN
2s8tZ/6LC3X82fajpVsYqF1xqEuUDndDXVD09E4u83MS6HO6a3bIVQDp6/klnYld
iE6Vp8HH5BSKaCWKVg8lGWg1UM9wZFnlryi14KgmpIFmcu9nA8yV/6MZAe6RSDmb
3iyNBmiZ8aZhGw2pI1YwR+15MVqFFGB+7ExkziROi7L8CFCyCezK2/oOOvQsH1dz
Q8z1JXWdg8/9Zx7Ktvgwu5PQM3cJtSHX6iBPOkMU8Z8TugLlTqQXKZOEgwajwvQ5
mf2DPkVgM08XAgaLJcLigwD513koAdtJd5v+9irw+5LAuO3JclqwTvwy7u/YwwID
AQABo1AwTjAdBgNVHQ4EFgQUo5A2tIu+bcUfvGTD7wmEkhXKFjcwHwYDVR0jBBgw
FoAUo5A2tIu+bcUfvGTD7wmEkhXKFjcwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B
AQsFAAOCAQEAWJ2rS6Mvlqk3GfEpboezx2J3X7l1z8Sxoqg6ntwB+rezvK3mc9H0
83qcVeUcoH+0A0lSHyFN4FvRQL6X1hEheHarYwJK4agb231vb5erasuGO463eYEG
r4SfTuOm7SyiV2xxbaBKrXJtpBp4WLL/s+LF+nklKjaOxkmxUX0sM4CTA7uFJypY
c8Tdr8lDDNqoUtMD8BrUCJi+7lmMXRcC3Qi3oZJW76ja+kZA5mKVFPd1ATih8TbA
i34R7EQDtFeiSvBdeKRsPp8c0KT8H1B4lXNkkCQs2WX5p4lm99+ZtLD4glw8x6Ic
i1YhgnQbn5E0hz55OLu5jvOkKQjPCW+9Aa==
-----END CERTIFICATE-----
EOT

}


resource "opentelekomcloud_lb_listener_v2" "elb_listener" {
name = "test"
loadbalancer_id = opentelekomcloud_lb_loadbalancer_v2.elb_public.id
protocol = "TERMINATED_HTTPS"
protocol_port = "443"
http2_enable = true
tls_ciphers_policy = "tls-1-0"
default_tls_container_ref = opentelekomcloud_lb_certificate_v2.elb_certificates[0].id
sni_container_refs = [
for s in opentelekomcloud_lb_certificate_v2.elb_certificates: s.id
]
}
`, env.OS_SUBNET_ID, count)
}
11 changes: 1 addition & 10 deletions opentelekomcloud/common/customize_diffs.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,6 @@ var (
elementListRegex = regexp.MustCompile(`^(.+?)\.\*\.(.+)$`)
)

func stringInSlice(str string, slice []string) bool {
for _, v := range slice {
if v == str {
return true
}
}
return false
}

func checkVolumeTypeAvailable(d cfg.SchemaOrDiff, argName, expectedAZ string, typeAZs map[string][]string) error {
volumeType := d.Get(argName)
if volumeType == nil {
Expand All @@ -51,7 +42,7 @@ func checkVolumeTypeAvailable(d cfg.SchemaOrDiff, argName, expectedAZ string, ty
if len(validAZs) == 0 {
return fmt.Errorf("volume type `%s` doesn't exist", resourceVolType)
}
if !stringInSlice(expectedAZ, validAZs) {
if !StringInSlice(expectedAZ, validAZs) {
return fmt.Errorf(
"volume type `%v` is not supported in AZ `%s`.\nSupported AZs: %v",
volumeType, expectedAZ, validAZs,
Expand Down
9 changes: 9 additions & 0 deletions opentelekomcloud/common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,12 @@ func GetAllAvailableZones(d *schema.ResourceData) []string {

return zones
}

func StringInSlice(str string, slice []string) bool {
for _, v := range slice {
if v == str {
return true
}
}
return false
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package elb

import (
"encoding/json"
"fmt"
"log"
"time"
Expand All @@ -9,9 +10,9 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"

"github.com/opentelekomcloud/gophertelekomcloud"
"github.com/opentelekomcloud/gophertelekomcloud/openstack/networking/v2/extensions/lbaas_v2/certificates"

"github.com/opentelekomcloud/gophertelekomcloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
"github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/common"
"github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/common/cfg"
)
Expand Down Expand Up @@ -184,27 +185,90 @@ func resourceCertificateV2Update(d *schema.ResourceData, meta interface{}) error

func resourceCertificateV2Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*cfg.Config)
networkingClient, err := config.NetworkingV2Client(config.GetRegion(d))
client, err := config.NetworkingV2Client(config.GetRegion(d))
if err != nil {
return fmt.Errorf("error creating OpenTelekomCloud networking client: %s", err)
}

log.Printf("[DEBUG] Deleting certificate %s", d.Id())
timeout := d.Timeout(schema.TimeoutDelete)
err = resource.Retry(timeout, func() *resource.RetryError {
err := certificates.Delete(networkingClient, d.Id()).ExtractErr()
err := certificates.Delete(client, d.Id()).ExtractErr()
if err != nil {
return common.CheckForRetryableError(err)
}
return nil
})
if err != nil {
if common.IsResourceNotFound(err) {
log.Printf("[INFO] deleting an unavailable certificate: %s", d.Id())
return handleCertificateDeletionError(d, client, err)
}

return nil
}

func handleCertificateDeletionError(d *schema.ResourceData, client *golangsdk.ServiceClient, err error) error {
if common.IsResourceNotFound(err) {
log.Printf("[INFO] deleting an unavailable certificate: %s", d.Id())
return nil
}

if err409, ok := err.(golangsdk.ErrDefault409); ok { // certificate in use
var dep struct {
ListenerIDs []string `json:"listener_ids"`
}
if err := json.Unmarshal(err409.Body, &dep); err != nil {
return fmt.Errorf("error loading assigned listeners: %w", err)
}

mErr := new(multierror.Error)
for _, listenerID := range dep.ListenerIDs {
mErr = multierror.Append(mErr, unassignCertWithRetry(client, d.Timeout(schema.TimeoutUpdate), d.Id(), listenerID))
}
if mErr.ErrorOrNil() != nil {
return mErr
}

log.Printf("[DEBUG] Retry deleting certificate %s", d.Id())
timeout := d.Timeout(schema.TimeoutDelete)
err := resource.Retry(timeout, func() *resource.RetryError {
err := certificates.Delete(client, d.Id()).ExtractErr()
if err != nil {
return common.CheckForRetryableError(err)
}
return nil
})
if err != nil {
return fmt.Errorf("error retrying certificate delition: %w", err)
}
return fmt.Errorf("error deleting certificate %s: %s", d.Id(), err)
return nil
}
return fmt.Errorf("error deleting certificate %s: %w", d.Id(), err)
}

func unassignCertWithRetry(client *golangsdk.ServiceClient, timeout time.Duration, certID, listenerID string) error {
listener, err := listeners.Get(client, listenerID).Extract()
if err != nil {
return fmt.Errorf("failed to get listener %s: %w", listenerID, err)
}

var otherCerts []string
for _, cert := range listener.SniContainerRefs {
if cert != certID {
otherCerts = append(otherCerts, cert)
}
}
opts := listeners.UpdateOpts{
SniContainerRefs: otherCerts,
}
err = resource.Retry(timeout, func() *resource.RetryError {
_, err := listeners.Update(client, listener.ID, opts).Extract()
if err != nil {
return common.CheckForRetryableError(err)
}
return nil
})
if err != nil {
return fmt.Errorf("error unassigning certificate %s from listener %s: %w", certID, listener.ID, err)
}
return nil
}