From 8ccd38013e6e92ef4164db3667b1b86e90919879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Pinson?= Date: Wed, 15 Mar 2017 15:22:34 +0100 Subject: [PATCH] Add rancher_certificate resource type (#12717) --- builtin/providers/rancher/provider.go | 1 + .../rancher/resource_rancher_certificate.go | 277 ++++++++++++++++++ .../rancher/r/certificate.html.markdown | 66 +++++ website/source/layouts/rancher.erb | 3 + 4 files changed, 347 insertions(+) create mode 100644 builtin/providers/rancher/resource_rancher_certificate.go create mode 100644 website/source/docs/providers/rancher/r/certificate.html.markdown diff --git a/builtin/providers/rancher/provider.go b/builtin/providers/rancher/provider.go index 3025aac8c9..9c176943f5 100644 --- a/builtin/providers/rancher/provider.go +++ b/builtin/providers/rancher/provider.go @@ -49,6 +49,7 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ + "rancher_certificate": resourceRancherCertificate(), "rancher_environment": resourceRancherEnvironment(), "rancher_host": resourceRancherHost(), "rancher_registration_token": resourceRancherRegistrationToken(), diff --git a/builtin/providers/rancher/resource_rancher_certificate.go b/builtin/providers/rancher/resource_rancher_certificate.go new file mode 100644 index 0000000000..09692465c4 --- /dev/null +++ b/builtin/providers/rancher/resource_rancher_certificate.go @@ -0,0 +1,277 @@ +package rancher + +import ( + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + rancher "github.com/rancher/go-rancher/client" +) + +func resourceRancherCertificate() *schema.Resource { + return &schema.Resource{ + Create: resourceRancherCertificateCreate, + Read: resourceRancherCertificateRead, + Update: resourceRancherCertificateUpdate, + Delete: resourceRancherCertificateDelete, + Importer: &schema.ResourceImporter{ + State: resourceRancherCertificateImport, + }, + + Schema: map[string]*schema.Schema{ + "id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "environment_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "cert": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "cert_chain": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "key": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "cn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "algorithm": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "cert_fingerprint": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "expires_at": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "issued_at": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "issuer": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "key_size": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "serial_number": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "subject_alternative_names": &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + }, + "version": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceRancherCertificateCreate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[INFO][rancher] Creating Certificate: %s", d.Id()) + client, err := meta.(*Config).EnvironmentClient(d.Get("environment_id").(string)) + if err != nil { + return err + } + + name := d.Get("name").(string) + description := d.Get("description").(string) + cert := d.Get("cert").(string) + certChain := d.Get("cert_chain").(string) + key := d.Get("key").(string) + + certificate := rancher.Certificate{ + Name: name, + Description: description, + Cert: cert, + CertChain: certChain, + Key: key, + } + newCertificate, err := client.Certificate.Create(&certificate) + if err != nil { + return err + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"active", "removed", "removing"}, + Target: []string{"active"}, + Refresh: CertificateStateRefreshFunc(client, newCertificate.Id), + Timeout: 10 * time.Minute, + Delay: 1 * time.Second, + MinTimeout: 3 * time.Second, + } + _, waitErr := stateConf.WaitForState() + if waitErr != nil { + return fmt.Errorf( + "Error waiting for registry credential (%s) to be created: %s", newCertificate.Id, waitErr) + } + + d.SetId(newCertificate.Id) + log.Printf("[INFO] Certificate ID: %s", d.Id()) + + return resourceRancherCertificateUpdate(d, meta) +} + +func resourceRancherCertificateRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[INFO] Refreshing Certificate: %s", d.Id()) + client, err := meta.(*Config).EnvironmentClient(d.Get("environment_id").(string)) + if err != nil { + return err + } + + certificate, err := client.Certificate.ById(d.Id()) + if err != nil { + return err + } + + log.Printf("[INFO] Certificate Name: %s", certificate.Name) + + d.Set("description", certificate.Description) + d.Set("name", certificate.Name) + + // Computed values + d.Set("cn", certificate.CN) + d.Set("algorithm", certificate.Algorithm) + d.Set("cert_fingerprint", certificate.CertFingerprint) + d.Set("expires_at", certificate.ExpiresAt) + d.Set("issued_at", certificate.IssuedAt) + d.Set("issuer", certificate.Issuer) + d.Set("key_size", certificate.KeySize) + d.Set("serial_number", certificate.SerialNumber) + d.Set("subject_alternative_names", certificate.SubjectAlternativeNames) + d.Set("version", certificate.Version) + + return nil +} + +func resourceRancherCertificateUpdate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[INFO] Updating Certificate: %s", d.Id()) + client, err := meta.(*Config).EnvironmentClient(d.Get("environment_id").(string)) + if err != nil { + return err + } + + certificate, err := client.Certificate.ById(d.Id()) + if err != nil { + return err + } + + name := d.Get("name").(string) + description := d.Get("description").(string) + cert := d.Get("cert").(string) + certChain := d.Get("cert_chain").(string) + key := d.Get("key").(string) + + data := map[string]interface{}{ + "name": &name, + "description": &description, + "cert": &cert, + "cert_chain": &certChain, + "key": &key, + } + + var newCertificate rancher.Certificate + if err := client.Update("certificate", &certificate.Resource, data, &newCertificate); err != nil { + return err + } + + return resourceRancherCertificateRead(d, meta) +} + +func resourceRancherCertificateDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[INFO] Deleting Certificate: %s", d.Id()) + id := d.Id() + client, err := meta.(*Config).EnvironmentClient(d.Get("environment_id").(string)) + if err != nil { + return err + } + + certificate, err := client.Certificate.ById(id) + if err != nil { + return err + } + + if err := client.Certificate.Delete(certificate); err != nil { + return fmt.Errorf("Error deleting Certificate: %s", err) + } + + log.Printf("[DEBUG] Waiting for certificate (%s) to be removed", id) + + stateConf := &resource.StateChangeConf{ + Pending: []string{"active", "removed", "removing"}, + Target: []string{"removed"}, + Refresh: CertificateStateRefreshFunc(client, id), + Timeout: 10 * time.Minute, + Delay: 1 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, waitErr := stateConf.WaitForState() + if waitErr != nil { + return fmt.Errorf( + "Error waiting for certificate (%s) to be removed: %s", id, waitErr) + } + + d.SetId("") + return nil +} + +func resourceRancherCertificateImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + envID, resourceID := splitID(d.Id()) + d.SetId(resourceID) + if envID != "" { + d.Set("environment_id", envID) + } else { + client, err := meta.(*Config).GlobalClient() + if err != nil { + return []*schema.ResourceData{}, err + } + stack, err := client.Environment.ById(d.Id()) + if err != nil { + return []*schema.ResourceData{}, err + } + d.Set("environment_id", stack.AccountId) + } + return []*schema.ResourceData{d}, nil +} + +// CertificateStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch +// a Rancher Certificate. +func CertificateStateRefreshFunc(client *rancher.RancherClient, certificateID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + cert, err := client.Certificate.ById(certificateID) + + if err != nil { + return nil, "", err + } + + return cert, cert.State, nil + } +} diff --git a/website/source/docs/providers/rancher/r/certificate.html.markdown b/website/source/docs/providers/rancher/r/certificate.html.markdown new file mode 100644 index 0000000000..bc0d445ded --- /dev/null +++ b/website/source/docs/providers/rancher/r/certificate.html.markdown @@ -0,0 +1,66 @@ +--- +layout: "rancher" +page_title: "Rancher: rancher_certificate" +sidebar_current: "docs-rancher-resource-certificate" +description: |- + Provides a Rancher Certificate resource. This can be used to create certificates for rancher environments and retrieve their information. +--- + +# rancher\_certificate + +Provides a Rancher Certificate resource. This can be used to create certificates for rancher environments and retrieve their information. + +## Example Usage + +```hcl +# Create a new Rancher Certificate +resource rancher_certificate "foo" { + name = "foo" + description = "my foo certificate" + environment_id = "${rancher_environment.test.id}" + cert = "${file("server.crt")}" + key = "${file("server.key")}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the registry credential. +* `description` - (Optional) A registry credential description. +* `environment_id` - (Required) The ID of the environment to create the certificate for. +* `cert` - (Required) The certificate content. +* `cert_chain` - (Optional) The certificate chain. +* `key` - (Required) The certificate key. + +## Attributes Reference + +The following attributes are exported: + +* `cn` - The certificate CN. +* `algorithm` - The certificate algorithm. +* `cert_fingerprint` - The certificate fingerprint. +* `expires_at` - The certificate expiration date. +* `issued_at` - The certificate creation date. +* `issuer` - The certificate issuer. +* `key_size` - The certificate key size. +* `serial_number` - The certificate serial number. +* `subject_alternative_names` - The list of certificate Subject Alternative Names. +* `version` - The certificate version. + +## Import + +Registry credentials can be imported using the Registry and credentials +IDs in the format `/` + +``` +$ terraform import rancher_certificate.mycert 1sp31/1c605 +``` + +If the credentials for the Rancher provider have access to the global API, +then `environment_id` can be omitted e.g. + +``` +$ terraform import rancher_certificate.mycert 1c605 +``` diff --git a/website/source/layouts/rancher.erb b/website/source/layouts/rancher.erb index 3dcd23373e..48a6d25d42 100644 --- a/website/source/layouts/rancher.erb +++ b/website/source/layouts/rancher.erb @@ -13,6 +13,9 @@ > Resources