Skip to content

Commit

Permalink
Implment resource rebooter
Browse files Browse the repository at this point in the history
Signed-off-by: George Nikolopoulos <[email protected]>
  • Loading branch information
George Nikolopoulos committed Apr 30, 2020
1 parent d82d9b2 commit bd16f65
Show file tree
Hide file tree
Showing 5 changed files with 314 additions and 0 deletions.
1 change: 1 addition & 0 deletions citrixadc/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ func providerResources() map[string]*schema.Resource {
"citrixadc_auditmessageaction": resourceCitrixAdcAuditmessageaction(),
"citrixadc_auditsyslogaction": resourceCitrixAdcAuditsyslogaction(),
"citrixadc_auditsyslogpolicy": resourceCitrixAdcAuditsyslogpolicy(),
"citrixadc_rebooter": resourceCitrixAdcRebooter(),
}
}

Expand Down
186 changes: 186 additions & 0 deletions citrixadc/resource_citrixadc_rebooter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package citrixadc

import (
"github.com/chiradeep/go-nitro/config/ns"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"

"bytes"
"fmt"
"log"
"net/http"
"strings"
"time"
)

func resourceCitrixAdcRebooter() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
Create: createRebooterFunc,
Read: schema.Noop,
Delete: schema.Noop,
Schema: map[string]*schema.Schema{
"warm": &schema.Schema{
Type: schema.TypeBool,
Required: true,
ForceNew: true,
},
"timestamp": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"wait_until_reachable": &schema.Schema{
Type: schema.TypeBool,
Required: true,
ForceNew: true,
},
"reachable_timeout": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "10m",
ForceNew: true,
},
"reachable_poll_delay": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "60s",
ForceNew: true,
},
"reachable_poll_interval": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "60s",
ForceNew: true,
},
"reachable_poll_timeout": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "20s",
ForceNew: true,
},
},
}
}

func createRebooterFunc(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] citrixadc-provider: In createRebooterFunc")
rebooterId := resource.PrefixedUniqueId("tf-rebooter-")

err := rebooterRebootAdcInstance(d, meta)
if err != nil {
return err
}

if d.Get("wait_until_reachable").(bool) {
err := rebooterWaitReachable(d, meta)
if err != nil {
return err
}
}

d.SetId(rebooterId)

return nil
}

func rebooterRebootAdcInstance(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] citrixadc-provider: In rebooterRebootAdcInstance")

client := meta.(*NetScalerNitroClient).client
reboot := ns.Reboot{
Warm: d.Get("warm").(bool),
}
if err := client.ActOnResource("reboot", &reboot, ""); err != nil {
return err
}
return nil
}

func rebooterWaitReachable(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] citrixadc-provider: In rebooterWaitReachable")

var err error
var timeout time.Duration
if timeout, err = time.ParseDuration(d.Get("reachable_timeout").(string)); err != nil {
return err
}

var poll_interval time.Duration
if poll_interval, err = time.ParseDuration(d.Get("reachable_poll_interval").(string)); err != nil {
return err
}

var poll_delay time.Duration
if poll_delay, err = time.ParseDuration(d.Get("reachable_poll_delay").(string)); err != nil {
return err
}
stateConf := &resource.StateChangeConf{
Pending: []string{"unreachable"},
Target: []string{"reachable"},
Refresh: rebooterInstancePoll(d, meta),
Timeout: timeout,
PollInterval: poll_interval,
Delay: poll_delay,
}

_, err = stateConf.WaitForState()
if err != nil {
return err
}

return nil
}

func rebooterPollLicense(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] citrixadc-provider: In rebooterPollLicense")

username := meta.(*NetScalerNitroClient).Username
password := meta.(*NetScalerNitroClient).Password
endpoint := meta.(*NetScalerNitroClient).Endpoint
url := fmt.Sprintf("%s/nitro/v1/config/nslicense", endpoint)

var timeout time.Duration
var err error
if timeout, err = time.ParseDuration(d.Get("reachable_poll_timeout").(string)); err != nil {
return err
}
c := http.Client{
Timeout: timeout,
}
buff := &bytes.Buffer{}
req, _ := http.NewRequest("GET", url, buff)
req.Header.Set("X-NITRO-USER", username)
req.Header.Set("X-NITRO-PASS", password)
resp, err := c.Do(req)
if err != nil {
if !strings.Contains(err.Error(), "Client.Timeout exceeded") {
// Unexpected error
return err
} else {
// Expected timeout error
return fmt.Errorf("Timeout")
}
} else {
log.Printf("Status code is %v\n", resp.Status)
}
// No error
return nil
}

func rebooterInstancePoll(d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
log.Printf("[DEBUG] citrixadc-provider: In reboooterInstancePoll")
err := rebooterPollLicense(d, meta)
if err != nil {
if err.Error() == "Timeout" {
return nil, "unreachable", nil
} else {
return nil, "unreachable", err
}
}
log.Printf("[DEBUG] citrixadc-provider: Returning \"reachable\"")
return "reachable", "reachable", nil
}
}
106 changes: 106 additions & 0 deletions citrixadc/resource_citrixadc_rebooter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
Copyright 2016 Citrix Systems, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package citrixadc

import (
"fmt"
"github.com/chiradeep/go-nitro/netscaler"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"testing"
)

func TestAccReboot_basic(t *testing.T) {
if isCluster {
t.Skip("Cluster does not support reboot operation")
}
if isCpxRun {
t.Skip("CPX does not support reboot operation")
}
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckRebootDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccRebooter_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckRebooterExist("citrixadc_rebooter.tf_rebooter", nil),
),
},
},
})
}

func testAccCheckRebooterExist(n string, id *string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No id is set")
}

if id != nil {
if *id != "" && *id != rs.Primary.ID {
return fmt.Errorf("Resource ID has changed!")
}

*id = rs.Primary.ID
}

return nil
}
}

func testAccCheckRebootDestroy(s *terraform.State) error {
nsClient := testAccProvider.Meta().(*NetScalerNitroClient).client

for _, rs := range s.RootModule().Resources {
if rs.Type != "citrixadc_reboot" {
continue
}

if rs.Primary.ID == "" {
return fmt.Errorf("No name is set")
}

_, err := nsClient.FindResource(netscaler.Reboot.Type(), rs.Primary.ID)
if err == nil {
return fmt.Errorf("LB vserver %s still exists", rs.Primary.ID)
}

}

return nil
}

const testAccRebooter_basic = `
resource "citrixadc_rebooter" "tf_rebooter" {
timestamp = "somethingrandom"
warm = true
wait_until_reachable = true
reachable_timeout = "10m"
reachable_poll_delay = "60s"
reachable_poll_interval = "60s"
reachable_poll_timeout = "20s"
}
`
3 changes: 3 additions & 0 deletions examples/rebooter/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
provider "citrixadc" {
endpoint = "http://localhost:8080"
}
18 changes: 18 additions & 0 deletions examples/rebooter/resources.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
resource "citrixadc_rebooter" "tf_rebooter" {
timestamp = timestamp()
warm = false
wait_until_reachable = true


# Wait for 10m in total
reachable_timeout = "10m"

# First poll after 60s
reachable_poll_delay = "60s"

# Subsequent polls each 60s
reachable_poll_interval = "60s"

# Timeout each single HTTP request after 20s
reachable_poll_timeout = "20s"
}

0 comments on commit bd16f65

Please sign in to comment.