chef_data_bag_item resource.

This commit is contained in:
Martin Atkins 2015-08-27 10:02:25 -07:00
parent d583b936b2
commit 406aba4a62
3 changed files with 234 additions and 1 deletions

View File

@ -1,6 +1,7 @@
package chef package chef
import ( import (
"encoding/json"
"io/ioutil" "io/ioutil"
"os" "os"
"time" "time"
@ -44,7 +45,7 @@ func Provider() terraform.ResourceProvider {
//"chef_client": resourceChefClient(), //"chef_client": resourceChefClient(),
//"chef_cookbook": resourceChefCookbook(), //"chef_cookbook": resourceChefCookbook(),
"chef_data_bag": resourceChefDataBag(), "chef_data_bag": resourceChefDataBag(),
//"chef_data_bag_item": resourceChefDataBagItem(), "chef_data_bag_item": resourceChefDataBagItem(),
//"chef_environment": resourceChefEnvironment(), //"chef_environment": resourceChefEnvironment(),
//"chef_node": resourceChefNode(), //"chef_node": resourceChefNode(),
//"chef_role": resourceChefRole(), //"chef_role": resourceChefRole(),
@ -77,3 +78,20 @@ func providerPrivateKeyEnvDefault() (interface{}, error) {
return nil, nil return nil, nil
} }
func jsonStateFunc(value interface{}) string {
// Parse and re-stringify the JSON to make sure it's always kept
// in a normalized form.
in, ok := value.(string)
if !ok {
return "null"
}
var tmp map[string]interface{}
// Assuming the value must be valid JSON since it passed okay through
// our prepareDataBagItemContent function earlier.
json.Unmarshal([]byte(in), &tmp)
jsonValue, _ := json.Marshal(&tmp)
return string(jsonValue)
}

View File

@ -0,0 +1,120 @@
package chef
import (
"encoding/json"
"fmt"
"github.com/hashicorp/terraform/helper/schema"
chefc "github.com/go-chef/chef"
)
func resourceChefDataBagItem() *schema.Resource {
return &schema.Resource{
Create: CreateDataBagItem,
Read: ReadDataBagItem,
Delete: DeleteDataBagItem,
Schema: map[string]*schema.Schema{
"data_bag_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"content_json": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
StateFunc: jsonStateFunc,
},
"id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}
func CreateDataBagItem(d *schema.ResourceData, meta interface{}) error {
client := meta.(*chefc.Client)
dataBagName := d.Get("data_bag_name").(string)
itemId, itemContent, err := prepareDataBagItemContent(d.Get("content_json").(string))
if err != nil {
return err
}
err = client.DataBags.CreateItem(dataBagName, itemContent)
if err != nil {
return err
}
d.SetId(itemId)
d.Set("id", itemId)
return nil
}
func ReadDataBagItem(d *schema.ResourceData, meta interface{}) error {
client := meta.(*chefc.Client)
// The Chef API provides no API to read a data bag's metadata,
// but we can try to read its items and use that as a proxy for
// whether it still exists.
itemId := d.Id()
dataBagName := d.Get("data_bag_name").(string)
value, err := client.DataBags.GetItem(dataBagName, itemId)
if err != nil {
if errRes, ok := err.(*chefc.ErrorResponse); ok {
if errRes.Response.StatusCode == 404 {
d.SetId("")
return nil
}
} else {
return err
}
}
jsonContent, err := json.Marshal(value)
if err != nil {
return err
}
d.Set("content_json", string(jsonContent))
return nil
}
func DeleteDataBagItem(d *schema.ResourceData, meta interface{}) error {
client := meta.(*chefc.Client)
itemId := d.Id()
dataBagName := d.Get("data_bag_name").(string)
err := client.DataBags.DeleteItem(dataBagName, itemId)
if err == nil {
d.SetId("")
d.Set("id", "")
}
return err
}
func prepareDataBagItemContent(contentJson string) (string, interface{}, error) {
var value map[string]interface{}
err := json.Unmarshal([]byte(contentJson), &value)
if err != nil {
return "", nil, err
}
var itemId string
if itemIdI, ok := value["id"]; ok {
itemId, _ = itemIdI.(string)
}
if itemId == "" {
return "", nil, fmt.Errorf("content_json must have id attribute, set to a string")
}
return itemId, value, nil
}

View File

@ -0,0 +1,95 @@
package chef
import (
"fmt"
"reflect"
"testing"
chefc "github.com/go-chef/chef"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccDataBagItem_basic(t *testing.T) {
var dataBagItemName string
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccDataBagItemCheckDestroy(dataBagItemName),
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDataBagItemConfig_basic,
Check: testAccDataBagItemCheck(
"chef_data_bag_item.test", &dataBagItemName,
),
},
},
})
}
func testAccDataBagItemCheck(rn string, name *string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[rn]
if !ok {
return fmt.Errorf("resource not found: %s", rn)
}
if rs.Primary.ID == "" {
return fmt.Errorf("data bag item id not set")
}
client := testAccProvider.Meta().(*chefc.Client)
content, err := client.DataBags.GetItem("terraform-acc-test-bag-item-basic", rs.Primary.ID)
if err != nil {
return fmt.Errorf("error getting data bag item: %s", err)
}
expectedContent := map[string]interface{}{
"id": "terraform_acc_test",
"something_else": true,
}
if !reflect.DeepEqual(content, expectedContent) {
return fmt.Errorf("wrong content: expected %#v, got %#v", expectedContent, content)
}
if expected := "terraform_acc_test"; rs.Primary.Attributes["id"] != expected {
return fmt.Errorf("wrong id; expected %#v, got %#v", expected, rs.Primary.Attributes["id"])
}
*name = rs.Primary.ID
return nil
}
}
func testAccDataBagItemCheckDestroy(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
client := testAccProvider.Meta().(*chefc.Client)
_, err := client.DataBags.GetItem("terraform-acc-test-bag-item-basic", name)
if err == nil {
return fmt.Errorf("data bag item still exists")
}
if _, ok := err.(*chefc.ErrorResponse); err != nil && !ok {
return fmt.Errorf("got something other than an HTTP error (%v) when getting data bag item", err)
}
return nil
}
}
const testAccDataBagItemConfig_basic = `
resource "chef_data_bag" "test" {
name = "terraform-acc-test-bag-item-basic"
}
resource "chef_data_bag_item" "test" {
data_bag_name = "terraform-acc-test-bag-item-basic"
depends_on = ["chef_data_bag.test"]
content_json = <<EOT
{
"id": "terraform_acc_test",
"something_else": true
}
EOT
}
`